This is the R vignette of the Bulk RNA-Seq Transcriptomics project. In this vignette we will start from a Recount table, whose counts derive from RNA-seq samples of human brain cells compared to pancreas and liver cells (see http://159.149.160.56/GT_files/July30/ and also https://gtexportal.org/home/).

Pre-processing steps

Datasets are in the “Ranged Summarized Experiment” format of Recount2.

library("recount", quietly = TRUE)

Let’s start from the first tissue mentioned: brain. We can load each dataset with the load() command.

data <- load("rse_gene_brain_9_scaled.Rdata")
data
[1] "rse"

The object containing the data is called “rse” and its type is “RangedSummarizedExperiment”.

rse
class: RangedSummarizedExperiment 
dim: 58037 10 
metadata(0):
assays(1): counts
rownames(58037): ENSG00000000003.14 ENSG00000000005.5 ... ENSG00000283698.1 ENSG00000283699.1
rowData names(3): gene_id bp_length symbol
colnames(10): SRR2166176 SRR2167642 ... SRR1397094 SRR817686
colData names(82): project sample ... title characteristics

Counts have already been normalized/scaled (to 40M reads per column), therefore they do not need:

#rse <- scale_counts(rse)

All row data are identical, they represent the Gencode V25 comprehensive annotation.

We can explore the information associated with the samples with colData, and with rows with rowData.

head(colData(rse), 2)
DataFrame with 2 rows and 82 columns
               project      sample  experiment         run read_count_as_reported_by_sra reads_downloaded
           <character> <character> <character> <character>                     <numeric>        <numeric>
SRR2166176   SRP012682  SRS1036203  SRX1152700  SRR2166176                     194283036        194280472
SRR2167642   SRP012682  SRS1036572  SRX1153642  SRR2167642                     178485738        178483520
           proportion_of_reads_reported_by_sra_downloaded paired_end sra_misreported_paired_end mapped_read_count         auc sharq_beta_tissue
                                                <numeric>  <logical>                  <logical>         <integer>   <numeric>         <logical>
SRR2166176                                       0.999987       TRUE                      FALSE         191059974 42413095962                NA
SRR2167642                                       0.999988       TRUE                      FALSE         175652255 38996160832                NA
           sharq_beta_cell_type biosample_submission_date biosample_publication_date biosample_update_date avg_read_length geo_accession
                      <logical>                 <logical>                  <logical>             <logical>       <integer>     <logical>
SRR2166176                   NA                        NA                         NA                    NA             500            NA
SRR2167642                   NA                        NA                         NA                    NA             500            NA
                      bigwig_file                 sampid  smatsscr    smcenter    smpthnts     smrin        smts                  smtsd     smubrid
                      <character>            <character> <integer> <character> <character> <numeric> <character>            <character> <character>
SRR2166176 SRR2166176_SRS103620.. GTEX-T5JC-0011-R11A-..        NA      C1, A1                   9.8       Brain Brain - Cerebellar H..     0002037
SRR2167642 SRR2167642_SRS103657.. GTEX-T6MN-0011-R11A-..        NA      C1, A1                   8.9       Brain Brain - Cerebellar H..     0002037
             smtspax   smtstptref    smnabtch              smnabtcht   smnabtchd    smgebtch     smafrze       smgtc  sme2mprt  smchmprs  smntrart
           <integer>  <character> <character>            <character> <character> <character> <character> <character> <numeric> <integer> <numeric>
SRR2166176        NA Actual Death    BP-24011 RNA isolation_PAXgen..  04/10/2012  LCSET-4990                          0.652753     17688  0.937885
SRR2167642        NA Actual Death    BP-24029 RNA isolation_PAXgen..  04/11/2012  LCSET-5404                          0.681510     18208  0.936907
            smnumgps   smmaprt  smexncrt  sm550nrm  smgnsdtc  smunmprt  sm350nrm  smrdlgth   smmncpb   sme1mmrt  smsflgth  smestlbs    smmppd
           <integer> <numeric> <numeric> <numeric> <integer> <numeric> <numeric> <integer> <numeric>  <numeric> <integer> <integer> <integer>
SRR2166176       500  0.740389  0.754849  0.296297     24696  0.603455  0.757195       250   198.658 0.00149830       290  68340470 134027135
SRR2167642       455  0.758914  0.732335  0.280716     25082  0.677837  0.835082       250   204.598 0.00148993       275  89331173 126114611
            smnterrt  smrrnanm   smrdttl   smvqcfl    smmncv  smtrscpt  smmppdpr  smcglgth  smgappct  smunpdrd  smntrnrt  smmpunrt  smexpeff
           <numeric> <integer> <integer> <integer> <numeric> <integer> <integer> <integer> <numeric> <integer> <numeric> <numeric> <numeric>
SRR2166176 0.0604812   2645283 181022622  13260414  0.638870    142012  57059850     44086 0.0251530         0  0.183036  0.446791  0.558882
SRR2167642 0.0615550   2434834 166177644  12308094  0.652932    142650  54698354     48115 0.0250529         0  0.204572  0.514420  0.555780
            smmppdun   sme2mmrt  sme2anti  smaltalg  sme2snse  smmflgth  sme1anti  smspltrd   smbsmmrt  sme1snse  sme1pcts  smrrnart  sme1mprt
           <integer>  <numeric> <integer> <integer> <integer> <integer> <integer> <integer>  <numeric> <integer> <numeric> <numeric> <numeric>
SRR2166176  80879363 0.00191255   1486357   5202496  47024056       403  59582880  32214781 0.00168091   1915592   3.11486  0.014613  0.828024
SRR2167642  85485135 0.00178416   1409790   4712790  44993677       401  55158090  29473960 0.00162204   1750181   3.07544  0.014652  0.836319
            smnum5cd  smdpmprt  sme2pcts     title characteristics
           <integer> <numeric> <numeric> <logical> <CharacterList>
SRR2166176       877  0.396545   96.9360        NA              NA
SRR2167642       855  0.322163   96.9619        NA              NA
rowData(rse)
DataFrame with 58037 rows and 3 columns
                              gene_id bp_length          symbol
                          <character> <integer> <CharacterList>
ENSG00000000003.14 ENSG00000000003.14      4535          TSPAN6
ENSG00000000005.5   ENSG00000000005.5      1610            TNMD
ENSG00000000419.12 ENSG00000000419.12      1207            DPM1
ENSG00000000457.13 ENSG00000000457.13      6883           SCYL3
ENSG00000000460.16 ENSG00000000460.16      5967        C1orf112
...                               ...       ...             ...
ENSG00000283695.1   ENSG00000283695.1        61              NA
ENSG00000283696.1   ENSG00000283696.1       997              NA
ENSG00000283697.1   ENSG00000283697.1      1184           HSFX3
ENSG00000283698.1   ENSG00000283698.1       940              NA
ENSG00000283699.1   ENSG00000283699.1        60         MIR4481

But also the genomic coordinates of genes.

rowRanges(rse)
GRanges object with 58037 ranges and 3 metadata columns:
                     seqnames              ranges strand |            gene_id bp_length          symbol
                        <Rle>           <IRanges>  <Rle> |        <character> <integer> <CharacterList>
  ENSG00000000003.14     chrX 100627109-100639991      - | ENSG00000000003.14      4535          TSPAN6
   ENSG00000000005.5     chrX 100584802-100599885      + |  ENSG00000000005.5      1610            TNMD
  ENSG00000000419.12    chr20   50934867-50958555      - | ENSG00000000419.12      1207            DPM1
  ENSG00000000457.13     chr1 169849631-169894267      - | ENSG00000000457.13      6883           SCYL3
  ENSG00000000460.16     chr1 169662007-169854080      + | ENSG00000000460.16      5967        C1orf112
                 ...      ...                 ...    ... .                ...       ...             ...
   ENSG00000283695.1    chr19   52865369-52865429      - |  ENSG00000283695.1        61            <NA>
   ENSG00000283696.1     chr1 161399409-161422424      + |  ENSG00000283696.1       997            <NA>
   ENSG00000283697.1     chrX 149548210-149549852      - |  ENSG00000283697.1      1184           HSFX3
   ENSG00000283698.1     chr2 112439312-112469687      - |  ENSG00000283698.1       940            <NA>
   ENSG00000283699.1    chr10   12653138-12653197      - |  ENSG00000283699.1        60         MIR4481
  -------
  seqinfo: 25 sequences (1 circular) from an unspecified genome; no seqlengths

Select the three columns we have to work on: 8 -> 8 9 10. What are their names?

rownames(colData(rse)[colnames(rse)[8:10],])
[1] "SRR1337909" "SRR1397094" "SRR817686" 

Total number of reads per column:

c(sum(assays(rse)$counts[,8]), sum(assays(rse)$counts[,9]), sum(assays(rse)$counts[,10]))
[1] 38280901 36625433 37946538

Remove all genes with length < 200, or, in other words, keep all genes with length >= 200…

rse <- rse[, 8:10]      
rse <- rse[rowData(rse)$bp_length >= 200,] 

and produce the overall read count associated with these genes in each tissue/replicate. Let us extract the count matrix from the rse object.

longerThan200 <- assays(rse)$counts
head(longerThan200)
                   SRR1337909 SRR1397094 SRR817686
ENSG00000000003.14        485        500       234
ENSG00000000005.5           8          1         1
ENSG00000000419.12        319        584       511
ENSG00000000457.13        297        332       461
ENSG00000000460.16        138        187       412
ENSG00000000938.12        146        241       837

Total number of reads tissue per column after removal:

c(sum(longerThan200[,1]), sum(longerThan200[,2]), sum(longerThan200[,3]))
[1] 38174026 36555701 37888352

Before removing all mitochondrial genes we have to check if these ones begin with MT.

unlist(rowData(rse)$symbol, use.names = FALSE)[grep("^MT-*", unlist(rowData(rse)$symbol, use.names = FALSE))] 
  [1] "MTMR7"     "MTMR11"    "MTA3"      "MTMR1"     "MTHFD2"    "MTFR1"     "MTIF2"     "MTMR2"     "MT3"       "MTAP"      "MTMR3"    
 [12] "MTHFD1"    "MTG2"      "MTMR8"     "MT4"       "MTHFSD"    "MTFMT"     "MTMR9"     "MTPN"      "MTPAP"     "MTMR4"     "MTCH2"    
 [23] "MTRF1L"    "MTR"       "MTFR1L"    "MTHFD1L"   "MTRF1"     "MTERF2"    "MTIF3"     "MTERF4"    "MTRR"      "MT1G"      "MT2A"     
 [34] "MTERF1"    "MTX2"      "MTUS1"     "MTSS1L"    "MTUS2"     "MTNR1B"    "MTO1"      "MTHFS"     "MTCH1"     "MTTP"      "MTMR6"    
 [45] "MTF2"      "MTFR2"     "MTDH"      "MTG1"      "MTA2"      "MTMR12"    "MTERF3"    "MTMR14"    "MTHFD2L"   "MTMR10"    "MTNR1A"   
 [56] "MTCL1"     "MT1B"      "MT1E"      "MTSS1"     "MTM1"      "MTBP"      "MTX1"      "MTHFR"     "MTX3"      "MTURN"     "MTA1"     
 [67] "MT1X"      "MTF1"      "MT1F"      "MTOR"      "MT1H"      "MT1A"      "MT1M"      "MTCP1"     "MTCYBP36"  "MTCYBP45"  "MTOR-AS1" 
 [78] "MTCYBP39"  "MTCYBP38"  "MTCYBP42"  "MTRNR2L4"  "MTCO2P34"  "MTCO1P57"  "MTCYBP34"  "MTFP1"     "MT1HL1"    "MTHFD2P1"  "MTCYBP44" 
 [89] "MTCYBP37"  "MTCYBP40"  "MTRNR2L5"  "MTCYBP43"  "MTCYBP35"  "MTCYBP41"  "MTRNR2L8"  "MT1JP"     "MTRNR2L10" "MTRNR2L3"  "MTRNR2L1" 
[100] "MTRNR2L7"  "MT1L"      "MTCYBP33"  "MTRNR2L2"  "MTRNR2L6"  "MTND1P37"  "MTND4LP32" "MTND5P42" 

Then we can keep !(mitochondrial genes)…

mt <- rowData(rse)$gene_id[grep("^MT-*", unlist(rowData(rse)$symbol, use.names = FALSE))]

and produce the overall read count associated with these genes in each tissue/replicate.

filtered1 <- assays(rse)$counts
filtered1 <- filtered1[which(!(rownames(filtered1) %in% mt)),]
head(filtered1)
                   SRR1337909 SRR1397094 SRR817686
ENSG00000000003.14        485        500       234
ENSG00000000005.5           8          1         1
ENSG00000000419.12        319        584       511
ENSG00000000457.13        297        332       461
ENSG00000000460.16        138        187       412
ENSG00000000938.12        146        241       837

Total number of reads tissue per column after removal:

c(sum(filtered1[,1]), sum(filtered1[,2]), sum(filtered1[,3]))
[1] 38114947 36483308 37797654

GEO information was absent for the SRP012682 dataset.

colData(rse)[, c("geo_accession", "title", "characteristics")]
DataFrame with 3 rows and 3 columns
           geo_accession     title characteristics
               <logical> <logical> <CharacterList>
SRR1337909            NA        NA              NA
SRR1397094            NA        NA              NA
SRR817686             NA        NA              NA

We can expand the biological metadata information by adding predictions based on RNA-seq data. The predictions include information about sex, sample source (cell line vs tissue), tissue and the sequencing strategy used.

rse <- add_predictions(rse)
2021-07-27 12:31:23 downloading the predictions to /var/folders/4k/0lqzcm0545d30b5nyr_dptyh0000gn/T//RtmpO7I0FM/PredictedPhenotypes_v0.0.06.rda
provo con l'URL 'https://github.com/leekgroup/recount-website/blob/master/predictions/PredictedPhenotypes_v0.0.06.rda?raw=true'
Content type 'application/octet-stream' length 548129 bytes (535 KB)
==================================================
downloaded 535 KB
Loading objects:
  PredictedPhenotypes
colData(rse)[, 83:ncol(colData(rse))]
DataFrame with 3 rows and 12 columns
           reported_sex predicted_sex accuracy_sex reported_samplesource predicted_samplesource accuracy_samplesource reported_tissue
               <factor>      <factor>    <numeric>              <factor>               <factor>             <numeric>        <factor>
SRR1337909       male          male       0.998113                tissue                 tissue              0.970958           Brain
SRR1397094       female        female     0.998113                tissue                 tissue              0.970958           Brain
SRR817686        male          male       0.998113                tissue                 tissue              0.970958           Brain
           predicted_tissue accuracy_tissue reported_sequencingstrategy predicted_sequencingstrategy accuracy_sequencingstrategy
                   <factor>       <numeric>                    <factor>                     <factor>                   <numeric>
SRR1337909            Brain        0.965611                      PAIRED                       PAIRED                    0.999371
SRR1397094            Brain        0.965611                      PAIRED                       PAIRED                    0.999371
SRR817686             Brain        0.965611                      PAIRED                       PAIRED                    0.999371

Project SRP012682 has a few extra biologically relevant variables via the SRA Run selector https://trace.ncbi.nlm.nih.gov/Traces/study/?acc=SRP012682.

sra <- read.csv("SraRunTable.txt", header = TRUE)

# Explore it
head(sra)
# Set all column names in lower case
colnames(sra) <- tolower(colnames(sra))

# Choose some meaningful variables we want to add
sra_vars <- c(
    "sex", "body_site", "histological_type", "is_tumor", "submitted_subject_id"
)
stopifnot(all(sra_vars %in% colnames(sra)))

# Re-organize the SRA table based on the SRA Run IDs we have
sra1 <- sra[match(colData(rse)$run, sra$run), ]

# Double check the order
stopifnot(identical(colData(rse)$run, as.character(sra1$run)))

# Append the variables of interest
colData(rse) <- cbind(colData(rse), sra1[, sra_vars])

colData(rse)[, 95:ncol(colData(rse))]
DataFrame with 3 rows and 5 columns
                   sex              body_site histological_type    is_tumor submitted_subject_id
           <character>            <character>       <character> <character>          <character>
SRR1337909        male Brain - Anterior cin..             Brain          No           GTEX-144GL
SRR1397094      female Brain - Nucleus accu..             Brain          No           GTEX-13X6K
SRR817686         male Brain - Substantia n..             Brain          No            GTEX-X4XX

Same steps for the other two tissues.

data <- load("rse_gene_pancreas_0_scaled.Rdata")
rownames(colData(rse)[colnames(rse)[3:5],])
[1] "SRR1374739" "SRR1097883" "SRR2167209"
c(sum(assays(rse)$counts[,3]), sum(assays(rse)$counts[,4]), sum(assays(rse)$counts[,5]))
[1] 37947698 37248113 36301737
rse <- rse[, 3:5]   # 3 -> 3 4 5.
rse <- rse[rowData(rse)$bp_length >= 200,] 
longerThan200 <- assays(rse)$counts
head(longerThan200)
                   SRR1374739 SRR1097883 SRR2167209
ENSG00000000003.14        644        555        448
ENSG00000000005.5           1          1          0
ENSG00000000419.12        534        790       1441
ENSG00000000457.13        568        476        491
ENSG00000000460.16        223        219        195
ENSG00000000938.12        631        154         53
c(sum(longerThan200[,1]), sum(longerThan200[,2]), sum(longerThan200[,3]))
[1] 37886305 37184058 36220949
#Check if mitochondrial genes begin with MT.
unlist(rowData(rse)$symbol, use.names = FALSE)[grep("^MT-*", unlist(rowData(rse)$symbol, use.names = FALSE))] 
  [1] "MTMR7"     "MTMR11"    "MTA3"      "MTMR1"     "MTHFD2"    "MTFR1"     "MTIF2"     "MTMR2"     "MT3"       "MTAP"      "MTMR3"    
 [12] "MTHFD1"    "MTG2"      "MTMR8"     "MT4"       "MTHFSD"    "MTFMT"     "MTMR9"     "MTPN"      "MTPAP"     "MTMR4"     "MTCH2"    
 [23] "MTRF1L"    "MTR"       "MTFR1L"    "MTHFD1L"   "MTRF1"     "MTERF2"    "MTIF3"     "MTERF4"    "MTRR"      "MT1G"      "MT2A"     
 [34] "MTERF1"    "MTX2"      "MTUS1"     "MTSS1L"    "MTUS2"     "MTNR1B"    "MTO1"      "MTHFS"     "MTCH1"     "MTTP"      "MTMR6"    
 [45] "MTF2"      "MTFR2"     "MTDH"      "MTG1"      "MTA2"      "MTMR12"    "MTERF3"    "MTMR14"    "MTHFD2L"   "MTMR10"    "MTNR1A"   
 [56] "MTCL1"     "MT1B"      "MT1E"      "MTSS1"     "MTM1"      "MTBP"      "MTX1"      "MTHFR"     "MTX3"      "MTURN"     "MTA1"     
 [67] "MT1X"      "MTF1"      "MT1F"      "MTOR"      "MT1H"      "MT1A"      "MT1M"      "MTCP1"     "MTCYBP36"  "MTCYBP45"  "MTOR-AS1" 
 [78] "MTCYBP39"  "MTCYBP38"  "MTCYBP42"  "MTRNR2L4"  "MTCO2P34"  "MTCO1P57"  "MTCYBP34"  "MTFP1"     "MT1HL1"    "MTHFD2P1"  "MTCYBP44" 
 [89] "MTCYBP37"  "MTCYBP40"  "MTRNR2L5"  "MTCYBP43"  "MTCYBP35"  "MTCYBP41"  "MTRNR2L8"  "MT1JP"     "MTRNR2L10" "MTRNR2L3"  "MTRNR2L1" 
[100] "MTRNR2L7"  "MT1L"      "MTCYBP33"  "MTRNR2L2"  "MTRNR2L6"  "MTND1P37"  "MTND4LP32" "MTND5P42" 
mt <- rowData(rse)$gene_id[grep("^MT-*", unlist(rowData(rse)$symbol, use.names = FALSE))]
filtered2 <- assays(rse)$counts
filtered2 <- filtered2[which(!(rownames(filtered2) %in% mt)),]
head(filtered2)
                   SRR1374739 SRR1097883 SRR2167209
ENSG00000000003.14        644        555        448
ENSG00000000005.5           1          1          0
ENSG00000000419.12        534        790       1441
ENSG00000000457.13        568        476        491
ENSG00000000460.16        223        219        195
ENSG00000000938.12        631        154         53
c(sum(filtered2[,1]), sum(filtered2[,2]), sum(filtered2[,3]))
[1] 37833065 37132317 36174592

Columns contain a lot of information, that permit to look up each sample in the GTEx database for further details, e.g. age/sex of the donor, a picture of the tissue sample itself, and so on (https://gtexportal.org/home/histologyPage). Unfortunately we could not appreciate those information before because “sampid”s of our three brain replicates are not present in the GTEx database.

colData(rse)$sampid
[1] "GTEX-139YR-1526-SM-5IFJ1" "GTEX-XBED-0226-SM-47JY8"  "GTEX-XXEK-1726-SM-5S2SR" 

All samples were collected from males between 50 and 69 yo all died from “ventilator case” (Hardy scale) and only the first had a fibrosis.

colData(rse)$smpthnts
[1] "2 pieces; islets well visualized, focal PanIN-1, some fibrosis"                                                                                             
[2] "2 pieces, 12.5x10 & 14x13mm; Scattered eosinophilic foci in exocrine and endocrine tissue, some having infarcted centers and fibrin thrombi in capillaries."
[3] "2 pieces, 9x8 & 10x9mm; extremely well preserved, minimal fat, Islets well visualized; representative ones delineated"                                      
rse <- add_predictions(rse)
2021-07-27 12:31:26 downloading the predictions to /var/folders/4k/0lqzcm0545d30b5nyr_dptyh0000gn/T//RtmpO7I0FM/PredictedPhenotypes_v0.0.06.rda
provo con l'URL 'https://github.com/leekgroup/recount-website/blob/master/predictions/PredictedPhenotypes_v0.0.06.rda?raw=true'
Content type 'application/octet-stream' length 548129 bytes (535 KB)
==================================================
downloaded 535 KB
Loading objects:
  PredictedPhenotypes
colData(rse)[, 83:ncol(colData(rse))]
DataFrame with 3 rows and 12 columns
           reported_sex predicted_sex accuracy_sex reported_samplesource predicted_samplesource accuracy_samplesource reported_tissue
               <factor>      <factor>    <numeric>              <factor>               <factor>             <numeric>        <factor>
SRR1374739         male          male     0.998113                tissue                 tissue              0.970958        Pancreas
SRR1097883         male          male     0.998113                tissue                 tissue              0.970958        Pancreas
SRR2167209         male          male     0.998113                tissue                 tissue              0.970958        Pancreas
           predicted_tissue accuracy_tissue reported_sequencingstrategy predicted_sequencingstrategy accuracy_sequencingstrategy
                   <factor>       <numeric>                    <factor>                     <factor>                   <numeric>
SRR1374739         Pancreas        0.965611                      PAIRED                       PAIRED                    0.999371
SRR1097883         Pancreas        0.965611                      PAIRED                       PAIRED                    0.999371
SRR2167209         Pancreas        0.965611                      PAIRED                       PAIRED                    0.999371
sra2 <- sra[match(colData(rse)$run, sra$run), ]

# Double check the order
stopifnot(identical(colData(rse)$run, as.character(sra2$run)))

# Append the variables of interest
colData(rse) <- cbind(colData(rse), sra2[, sra_vars])

colData(rse)[, 95:ncol(colData(rse))]
DataFrame with 3 rows and 5 columns
                   sex   body_site histological_type    is_tumor submitted_subject_id
           <character> <character>       <character> <character>          <character>
SRR1374739        male    Pancreas          Pancreas          No           GTEX-139YR
SRR1097883        male    Pancreas          Pancreas          No            GTEX-XBED
SRR2167209        male    Pancreas          Pancreas          No            GTEX-XXEK
data <- load("rse_gene_liver_8_scaled.Rdata")
rownames(colData(rse)[colnames(rse)[8:10],])
[1] "SRR1349479" "SRR1431248" "SRR1405054"
c(sum(assays(rse)$counts[,8]), sum(assays(rse)$counts[,9]), sum(assays(rse)$counts[,10]))
[1] 40990128 37822190 37364870
rse <- rse[, 8:10]  # 8 -> 8 9 10.
rse <- rse[rowData(rse)$bp_length >= 200,] 
longerThan200 <- assays(rse)$counts
head(longerThan200)
                   SRR1349479 SRR1431248 SRR1405054
ENSG00000000003.14       2258       1629       2190
ENSG00000000005.5           2          1          2
ENSG00000000419.12        974        657        546
ENSG00000000457.13        447        800        785
ENSG00000000460.16        343        439        323
ENSG00000000938.12       1252        591        259
c(sum(longerThan200[,1]), sum(longerThan200[,2]), sum(longerThan200[,3]))
[1] 40933056 37751464 37318324
#Check if mitochondrial genes begin with MT.
unlist(rowData(rse)$symbol, use.names = FALSE)[grep("^MT-*", unlist(rowData(rse)$symbol, use.names = FALSE))] 
  [1] "MTMR7"     "MTMR11"    "MTA3"      "MTMR1"     "MTHFD2"    "MTFR1"     "MTIF2"     "MTMR2"     "MT3"       "MTAP"      "MTMR3"    
 [12] "MTHFD1"    "MTG2"      "MTMR8"     "MT4"       "MTHFSD"    "MTFMT"     "MTMR9"     "MTPN"      "MTPAP"     "MTMR4"     "MTCH2"    
 [23] "MTRF1L"    "MTR"       "MTFR1L"    "MTHFD1L"   "MTRF1"     "MTERF2"    "MTIF3"     "MTERF4"    "MTRR"      "MT1G"      "MT2A"     
 [34] "MTERF1"    "MTX2"      "MTUS1"     "MTSS1L"    "MTUS2"     "MTNR1B"    "MTO1"      "MTHFS"     "MTCH1"     "MTTP"      "MTMR6"    
 [45] "MTF2"      "MTFR2"     "MTDH"      "MTG1"      "MTA2"      "MTMR12"    "MTERF3"    "MTMR14"    "MTHFD2L"   "MTMR10"    "MTNR1A"   
 [56] "MTCL1"     "MT1B"      "MT1E"      "MTSS1"     "MTM1"      "MTBP"      "MTX1"      "MTHFR"     "MTX3"      "MTURN"     "MTA1"     
 [67] "MT1X"      "MTF1"      "MT1F"      "MTOR"      "MT1H"      "MT1A"      "MT1M"      "MTCP1"     "MTCYBP36"  "MTCYBP45"  "MTOR-AS1" 
 [78] "MTCYBP39"  "MTCYBP38"  "MTCYBP42"  "MTRNR2L4"  "MTCO2P34"  "MTCO1P57"  "MTCYBP34"  "MTFP1"     "MT1HL1"    "MTHFD2P1"  "MTCYBP44" 
 [89] "MTCYBP37"  "MTCYBP40"  "MTRNR2L5"  "MTCYBP43"  "MTCYBP35"  "MTCYBP41"  "MTRNR2L8"  "MT1JP"     "MTRNR2L10" "MTRNR2L3"  "MTRNR2L1" 
[100] "MTRNR2L7"  "MT1L"      "MTCYBP33"  "MTRNR2L2"  "MTRNR2L6"  "MTND1P37"  "MTND4LP32" "MTND5P42" 
mt <- rowData(rse)$gene_id[grep("^MT-*", unlist(rowData(rse)$symbol, use.names = FALSE))]
filtered3 <- assays(rse)$counts
filtered3 <- filtered3[which(!(rownames(filtered3) %in% mt)),]
head(filtered3)
                   SRR1349479 SRR1431248 SRR1405054
ENSG00000000003.14       2258       1629       2190
ENSG00000000005.5           2          1          2
ENSG00000000419.12        974        657        546
ENSG00000000457.13        447        800        785
ENSG00000000460.16        343        439        323
ENSG00000000938.12       1252        591        259
c(sum(filtered3[,1]), sum(filtered3[,2]), sum(filtered3[,3]))
[1] 40849046 37656652 37204631
colData(rse)$sampid
[1] "GTEX-YB5E-0326-SM-5IFHU"  "GTEX-1269C-0626-SM-5FQSS" "GTEX-14AS3-0126-SM-5Q5F4"

The first replicate was collected from a man (40-49 yo) and it’s associated with “necrosis”, man died from ventilator case. Second one comes from a woman (60-69 yo) died from natural causes and the tissue shows congestion and steatosis. Last one comes from a woman died from ventilator case (40-49 yo) and the tissue shows no pathology.

colData(rse)$smpthnts
[1] "2 pieces, 12x10 & 11x11mm; marked central and mid-zonal  coagulative necrosis necrosis, shock vs toxic damage by drugs"
[2] "2 pieces, mild macrovesicular steatosis, passive congestion"                                                           
[3] "2 pieces"                                                                                                              
rse <- add_predictions(rse)
2021-07-27 12:31:28 downloading the predictions to /var/folders/4k/0lqzcm0545d30b5nyr_dptyh0000gn/T//RtmpO7I0FM/PredictedPhenotypes_v0.0.06.rda
provo con l'URL 'https://github.com/leekgroup/recount-website/blob/master/predictions/PredictedPhenotypes_v0.0.06.rda?raw=true'
Content type 'application/octet-stream' length 548129 bytes (535 KB)
==================================================
downloaded 535 KB
Loading objects:
  PredictedPhenotypes
colData(rse)[, 83:ncol(colData(rse))]
DataFrame with 3 rows and 12 columns
           reported_sex predicted_sex accuracy_sex reported_samplesource predicted_samplesource accuracy_samplesource reported_tissue
               <factor>      <factor>    <numeric>              <factor>               <factor>             <numeric>        <factor>
SRR1349479       male          male       0.998113                tissue                 tissue              0.970958           Liver
SRR1431248       female        female     0.998113                tissue                 tissue              0.970958           Liver
SRR1405054       female        female     0.998113                tissue                 tissue              0.970958           Liver
           predicted_tissue accuracy_tissue reported_sequencingstrategy predicted_sequencingstrategy accuracy_sequencingstrategy
                   <factor>       <numeric>                    <factor>                     <factor>                   <numeric>
SRR1349479            Liver        0.965611                      PAIRED                       PAIRED                    0.999371
SRR1431248            Liver        0.965611                      PAIRED                       PAIRED                    0.999371
SRR1405054            Liver        0.965611                      PAIRED                       PAIRED                    0.999371
# Re-organize the SRA table based on the SRA Run IDs we have
sra3 <- sra[match(colData(rse)$run, sra$run), ]

# Double check the order
stopifnot(identical(colData(rse)$run, as.character(sra3$run)))

# Append the variables of interest
colData(rse) <- cbind(colData(rse), sra3[, sra_vars])

colData(rse)[, 95:ncol(colData(rse))]
DataFrame with 3 rows and 5 columns
                   sex   body_site histological_type    is_tumor submitted_subject_id
           <character> <character>       <character> <character>          <character>
SRR1349479        male       Liver             Liver          No            GTEX-YB5E
SRR1431248      female       Liver             Liver          No           GTEX-1269C
SRR1405054      female       Liver             Liver          No           GTEX-14AS3

Merge them into a single count table/object, for subsequent analyses.

countTable <- merge(filtered1, filtered2, by = "row.names", all = TRUE)
rownames(countTable) <- countTable$Row.names
countTable$Row.names <- NULL
countTable <- data.matrix(countTable, rownames.force = NA)
countTable <- merge(countTable, filtered3, by = "row.names", all = TRUE)
rownames(countTable) <- countTable$Row.names
countTable$Row.names <- NULL
countTable <- data.matrix(countTable, rownames.force = NA)
head(countTable)
                   SRR1337909 SRR1397094 SRR817686 SRR1374739 SRR1097883 SRR2167209 SRR1349479 SRR1431248 SRR1405054
ENSG00000000003.14        485        500       234        644        555        448       2258       1629       2190
ENSG00000000005.5           8          1         1          1          1          0          2          1          2
ENSG00000000419.12        319        584       511        534        790       1441        974        657        546
ENSG00000000457.13        297        332       461        568        476        491        447        800        785
ENSG00000000460.16        138        187       412        223        219        195        343        439        323
ENSG00000000938.12        146        241       837        631        154         53       1252        591        259

Let’s prepare a table summarizing what we’ve done, one row per tissue/sample.

a <- c("Brain / SRR1337909", "Brain / SRR1397094", "Brain / SRR817686", "Pancreas / SRR1374739", "Pancreas / SRR1097883", "Pancreas / SRR2167209", "Liver / SRR1349479", "Liver / SRR1431248", "Liver / SRR1405054")
b <- c(38280901, 36625433, 37946538, 37947698, 37248113, 36301737, 40990128, 37822190, 37364870)
c <- c(106875, 69732, 58186, 61393, 64055, 80788, 57072, 70726, 46546)
d <- c(0.279186218, 0.190392288, 0.153336781, 0.161783199, 0.171968443, 0.222545824, 0.139233524, 0.186996046, 0.124571556)
e <- c(59079, 72393, 90698, 53240, 51741, 46357, 84010, 94812, 113693)
f <- c(0.154330223, 0.197657731, 0.239015216, 0.140298365, 0.13890905, 0.127699123, 0.204951787, 0.250678239, 0.304277788)
g <- c(sum(countTable[,1]), sum(countTable[,2]), sum(countTable[,3]), sum(countTable[,4]), sum(countTable[,5]), sum(countTable[,6]), sum(countTable[,7]), sum(countTable[,8]), sum(countTable[,9]))
tissuePerColumn <- data.frame(TissueColumn = a, TotalNumberOfReads = b, TotalNumberOfReadsOnShortRNAs = c, PercentageOfReadsOnShortRNAs = d, TotalNumberOfReadsOnMTGenes = e, PercentageOfReadsOnMTgenes = f, TotalNumberOfFilteredReads = g)
rownames(tissuePerColumn) <- tissuePerColumn$TissueColumn
tissuePerColumn$TissueColumn <- NULL
tissuePerColumn

DE genes using edgeR

The tool for calling DE genes we choose is edgeR.

library("edgeR")

We’ll process all tissues together, with a suitable design matrix. The row names of the table are the “annoying” ENSG IDs with the number at the end, so we clean them.

ensembl <- gsub("\\..*", "", rownames(countTable))
rownames(countTable) <- ensembl
head(countTable)
                SRR1337909 SRR1397094 SRR817686 SRR1374739 SRR1097883 SRR2167209 SRR1349479 SRR1431248 SRR1405054
ENSG00000000003        485        500       234        644        555        448       2258       1629       2190
ENSG00000000005          8          1         1          1          1          0          2          1          2
ENSG00000000419        319        584       511        534        790       1441        974        657        546
ENSG00000000457        297        332       461        568        476        491        447        800        785
ENSG00000000460        138        187       412        223        219        195        343        439        323
ENSG00000000938        146        241       837        631        154         53       1252        591        259

We know that the first three columns are brain cells, the subsequent three are pancreas cells, while the remaining three are liver cells. We give to edgeR the corresponding info.

group <- as.factor(rep(c("Brain","Pancreas","Liver"), c(3,3,3)))    

For edgeR the “DE gene” object containing all the info about the dataset as well as the parameters estimated during the different steps of the analysis is a “DGEList”; we add the counts to it.

#countTable[is.na(countTable)] = 0
y <- DGEList(counts=countTable)
y$samples$group <- group
y
An object of class "DGEList"
$counts
                SRR1337909 SRR1397094 SRR817686 SRR1374739 SRR1097883 SRR2167209 SRR1349479 SRR1431248 SRR1405054
ENSG00000000003        485        500       234        644        555        448       2258       1629       2190
ENSG00000000005          8          1         1          1          1          0          2          1          2
ENSG00000000419        319        584       511        534        790       1441        974        657        546
ENSG00000000457        297        332       461        568        476        491        447        800        785
ENSG00000000460        138        187       412        223        219        195        343        439        323
50587 more rows ...

$samples
NA
ensembl <- gsub(".*R","",rownames(y$samples))
barplot(y$samples$lib.size*1e-6, names=ensembl, ylab="Library size (millions)", las=2, col=rep(2:4, each=3))
legend("top", legend = c("Brain", "Pancreas", "Liver"), fill=rep(2:4, each=1), bty = "n", inset = c(-0.05,-0.1), xpd=TRUE, horiz=T)

Notice that “norm.factors” is still set to one. First, it is advisable to remove altogether all genes with low or zero counts.

table(rowSums(y$counts==0)==9)

FALSE  TRUE 
42550  8042 
keep.exprs <- filterByExpr(y, group=group)
y <- y[keep.exprs,, keep.lib.sizes=FALSE]

Function keep.exprs permits also to change the parameters of filtering. Let us extract and store in a vector the log of the counts per million before normalization with the “cpm” function.

logcpm_before <- cpm(y, log=TRUE)

We can now normalize the counts. TMM (trimmed mean of M-values) is recommended for most RNA-Seq data where the majority (more than half) of the genes are believed not differentially expressed between any pair of the samples.

y <- calcNormFactors(y, method = "TMM")
y
An object of class "DGEList"
$counts
                SRR1337909 SRR1397094 SRR817686 SRR1374739 SRR1097883 SRR2167209 SRR1349479 SRR1431248 SRR1405054
ENSG00000000003        485        500       234        644        555        448       2258       1629       2190
ENSG00000000419        319        584       511        534        790       1441        974        657        546
ENSG00000000457        297        332       461        568        476        491        447        800        785
ENSG00000000460        138        187       412        223        219        195        343        439        323
ENSG00000000938        146        241       837        631        154         53       1252        591        259
24257 more rows ...

$samples
NA

Notice now “norm.factors” has changed, and despite quite relevant differences in library sizes, they remain quite moderate. Distribution of (normalized) log-cpm values across samples:

logcpm <- cpm(y, log=TRUE)
boxplot(logcpm, las=2, names = ensembl, col=rep(2:4, each=3), notch = TRUE)
legend("top", legend = c("Brain", "Pancreas", "Liver"), fill=rep(2:4, each=1), bty = "n", inset = c(-0.05,-0.1), xpd=TRUE, horiz=T)

Boxplots of the log2(normalized_counts) of each sample, one boxplot per sample without outliers.

boxplot(logcpm, las=2, names = ensembl, col=rep(2:4, each=3), outline=FALSE , notch = TRUE)
legend("top", legend = c("Brain", "Pancreas", "Liver"), fill=rep(2:4, each=1), bty = "n", inset = c(-0.05,-0.1), xpd=TRUE, horiz=T)

And before normalization:

boxplot(logcpm_before, las=2, names = ensembl, col=rep(2:4, each=3), notch = TRUE)
legend("top", legend = c("Brain", "Pancreas", "Liver"), fill=rep(2:4, each=1), bty = "n", inset = c(-0.05,-0.1), xpd=TRUE, horiz=T)

The change is small, but it can be noticed by eye. Now we design the linear model; we don’t have to define an “intercept” term for our model. The intercept is on the origin since there is no “base condition” (e.g. cancer cells vs “normal” cells) from which the others can be related by a change.

design <- model.matrix(~0+group, data=y$samples)
colnames(design) <- levels(y$samples$group)
design
           Brain Liver Pancreas
SRR1337909     1     0        0
SRR1397094     1     0        0
SRR817686      1     0        0
SRR1374739     0     0        1
SRR1097883     0     0        1
SRR2167209     0     0        1
SRR1349479     0     1        0
SRR1431248     0     1        0
SRR1405054     0     1        0
attr(,"assign")
[1] 1 1 1
attr(,"contrasts")
attr(,"contrasts")$group
[1] "contr.treatment"

At this point, counts have been normalized and the design defined.

Exploratory analysis: we plot the samples labeled by group and then by replicate.

plotMDS(logcpm, labels=group, col=rep(2:4, each=3))

plotMDS(y, col=rep(2:4, each=3))    

MDS is a dimensionality reduction, in which instead of expression the fold ratio values among the three samples are employed. We now estimate the NB dispersion, and plot the BCV.

library(statmod)
y <- estimateDisp(y, design, robust=TRUE)
plotBCV(y, main="BCV Plot")

All the parameters have been stored in the y object, among which the “common” and gene-specific dispersion estimates.

y$common.dispersion
[1] 0.3094967

Now we fit the data to the “generalized linear” model.

fit <- glmQLFit(y, design, robust=TRUE)
plotQLDisp(fit)

For testing for DE genes, we have to specify the contrast. The following is condition 1 (brain) vs condition 2 (pancreas). topTags returns the genes with the highest variation.

qlf.1vs2 <- glmQLFTest(fit, contrast=c(1,0,-1)) # keep in mind grouping (see design matrix) follows alphabetical order, therefore 1 -> Brain 0 -> Liver -1 -> Pancreas
topTags(qlf.1vs2)
Coefficient:  1*Brain -1*Pancreas 

Plot log-fold change against log-counts per million, with DE genes highlighted:

plotMD(qlf.1vs2)
abline(h=c(1, 0, -1), col="blue")

The complete results of the test are in qlf.1vs2$table. Let us select the ones with p-value (FDR) < 0.05.

FDR <- p.adjust(qlf.1vs2$table$PValue, method="BH")
sum(FDR < 0.05)
[1] 7507

Or:

summary(decideTests(qlf.1vs2))
       1*Brain -1*Pancreas
Down                  3084
NotSig               16755
Up                    4423

decideTests has a default FDR (BH adjusted p-value) threshold of 0.05, and no check on the log-fold ratio. We can make the selection more stringent, by setting stricter thresholds for both and setting lfc (log-fold-change) = 1/-1.

summary(decideTests(qlf.1vs2, p.value=0.01, lfc=1))
       1*Brain -1*Pancreas
Down                  1536
NotSig               20502
Up                    2224

We retain p-value = 0.01 is a better and more significant value since most of the tools have a threshold around 2,000 for the maximum number of genes that can be submitted for any analysis of this kind.

Here we save the list of genes with adjusted p-value (FDR) lower than 0.01.

deg.1vs2 <- topTags(qlf.1vs2, n=Inf, adjust.method = "BH", sort.by = "PValue", p.value = 0.01)$table
head(deg.1vs2)

We can perform the comparision of condition 1 vs. 3 in a similar way.

qlf.1vs3 <- glmQLFTest(fit, contrast=c(1,-1,0))
deg.1vs3 <- topTags(qlf.1vs3, n=Inf, adjust.method = "BH", sort.by = "PValue", p.value = 0.01)$table
head(deg.1vs3)

But also contrast condition 2 vs. 3.

qlf.2vs3 <- glmQLFTest(fit, contrast=c(0,-1,1))
deg.2vs3 <- topTags(qlf.2vs3, n=Inf, adjust.method = "BH", sort.by = "PValue", p.value = 0.01)$table
head(deg.2vs3)

Now we have all our results, contained here.

head(qlf.1vs2$table)
head(qlf.1vs3$table)
head(qlf.2vs3$table)

Now we want to replace the ENSG IDs with the gene name/symbol, but instead of replacing the names in the table it is better to add an additional column to it. The org.Hs.eg.db is a R package containing the gene annotation for human (same for mouse if you replace Hs with Mm, and so on) in which for each gene in each annotation there is its correspondence to another annotation. We can employ it to “translate” gene IDs or names from one annotation to the other. We also add an “entrezid” column, useful for subsequent analysis since GO and KEGG (the tools we’ll use) work on that one.

library("org.Hs.eg.db")
qlf.1vs2$table$symbol <- mapIds(org.Hs.eg.db,keys=rownames(qlf.1vs2$table), keytype="ENSEMBL", column="SYMBOL", multiVals="first")          
'select()' returned 1:many mapping between keys and columns
qlf.1vs2$table$entrezid <- mapIds(org.Hs.eg.db, keys=rownames(qlf.1vs2$table), column="ENTREZID", keytype="ENSEMBL", multiVals="first")
'select()' returned 1:many mapping between keys and columns
qlf.1vs3$table$symbol <- mapIds(org.Hs.eg.db,keys=rownames(qlf.1vs3$table), keytype="ENSEMBL", column="SYMBOL", multiVals="first")
'select()' returned 1:many mapping between keys and columns
qlf.1vs3$table$entrezid <- mapIds(org.Hs.eg.db, keys=rownames(qlf.1vs3$table), column="ENTREZID", keytype="ENSEMBL", multiVals="first")
'select()' returned 1:many mapping between keys and columns
qlf.2vs3$table$symbol <- mapIds(org.Hs.eg.db,keys=rownames(qlf.2vs3$table), keytype="ENSEMBL", column="SYMBOL", multiVals="first")
'select()' returned 1:many mapping between keys and columns
qlf.2vs3$table$entrezid <- mapIds(org.Hs.eg.db, keys=rownames(qlf.2vs3$table), column="ENTREZID", keytype="ENSEMBL", multiVals="first")
'select()' returned 1:many mapping between keys and columns
head(qlf.1vs2$table)
head(qlf.1vs3$table)
head(qlf.2vs3$table)

Now we can select what we consider to be the “DE” genes from the table. We select them by corrected p-value first, then by FC (positive/negative).

deg.1vs2 <- topTags(qlf.1vs2, n=Inf, adjust.method = "BH", sort.by = "PValue", p.value = 0.01)$table
deg.1vs3 <- topTags(qlf.1vs3, n=Inf, adjust.method = "BH", sort.by = "PValue", p.value = 0.01)$table
deg.2vs3 <- topTags(qlf.2vs3, n=Inf, adjust.method = "BH", sort.by = "PValue", p.value = 0.01)$table
head(deg.1vs2)
head(deg.1vs3)
head(deg.2vs3)

And then retrieve the lists of “up” and “down” regulated ones.

up.genes.1vs2 <- deg.1vs2[deg.1vs2$logFC > 0,]
up.genes.1vs3 <- deg.1vs3[deg.1vs3$logFC > 0,]
up.genes.2vs3 <- deg.2vs3[deg.2vs3$logFC > 0,]
head(up.genes.1vs2)
head(up.genes.1vs3)
head(up.genes.2vs3)
down.genes.1vs2 <- deg.1vs2[deg.1vs2$logFC < 0,]
down.genes.1vs3 <- deg.1vs3[deg.1vs3$logFC < 0,]
down.genes.2vs3 <- deg.2vs3[deg.2vs3$logFC < 0,]
head(down.genes.1vs2)
head(down.genes.1vs3)
head(down.genes.2vs3)

Finally, we can save Excel sheets for further use.

library("xlsx")
write.xlsx(deg.1vs2, file="deg.1vs2.xlsx", sheetName = "a", col.names = TRUE, row.names = TRUE, append = FALSE)
write.xlsx(deg.1vs3, file="deg.1vs3.xlsx", sheetName = "b", col.names = TRUE, row.names = TRUE, append = FALSE)
write.xlsx(deg.2vs3, file="deg.2vs3.xlsx", sheetName = "c", col.names = TRUE, row.names = TRUE, append = FALSE)

#up 1vs23
l <- list(up.genes.1vs2, up.genes.1vs3)
common_names = Reduce(intersect, lapply(l, row.names))
l <- lapply(l, function(x) { x[row.names(x) %in% common_names,] })
library(dplyr)
l <- lapply(l, function(x) x%>% select(symbol,entrezid))
# l is a list containing two dataframes with same rownames, symbols & entrezids, as expected. You can check it using l1 <- l[[1]]; l1 <- l1[ order(row.names(l1)), ]; l2 <- l[[2]]; l2 <- l2[ order(row.names(l2)), ]; l1 == l2
# Therefore we can pick either l[[1]] or l[[2]]
write.xlsx(l[[1]], file="up.1vs23.xlsx", sheetName = "d-1vs23", col.names = TRUE, row.names = TRUE, append = FALSE) 

#up 2vs13
l <- list(up.genes.1vs2, up.genes.2vs3)
common_names = Reduce(intersect, lapply(l, row.names))
l <- lapply(l, function(x) { x[row.names(x) %in% common_names,] })
l <- lapply(l, function(x) x%>% select(symbol,entrezid))
write.xlsx(l[[1]], file="up.2vs13.xlsx", sheetName = "d-2vs13", col.names = TRUE, row.names = TRUE, append = FALSE) 

#up 3vs12
l <- list(up.genes.1vs3, up.genes.2vs3)
common_names = Reduce(intersect, lapply(l, row.names))
l <- lapply(l, function(x) { x[row.names(x) %in% common_names,] })
l <- lapply(l, function(x) x%>% select(symbol,entrezid))
write.xlsx(l[[1]], file="up.3vs12.xlsx", sheetName = "d-3vs12", col.names = TRUE, row.names = TRUE, append = FALSE)

#down 1vs23
l <- list(down.genes.1vs2, down.genes.1vs3)
common_names = Reduce(intersect, lapply(l, row.names))
l <- lapply(l, function(x) { x[row.names(x) %in% common_names,] })
l <- lapply(l, function(x) x%>% select(symbol,entrezid))
write.xlsx(l[[1]], file="down.1vs23.xlsx", sheetName = "e-1vs23", col.names = TRUE, row.names = TRUE, append = FALSE) 

#down 2vs13
l <- list(down.genes.1vs2, down.genes.2vs3)
common_names = Reduce(intersect, lapply(l, row.names))
l <- lapply(l, function(x) { x[row.names(x) %in% common_names,] })
l <- lapply(l, function(x) x%>% select(symbol,entrezid))
write.xlsx(l[[1]], file="down.2vs13.xlsx", sheetName = "e-2vs13", col.names = TRUE, row.names = TRUE, append = FALSE) 

#down 3vs12
l <- list(down.genes.1vs3, down.genes.2vs3)
common_names = Reduce(intersect, lapply(l, row.names))
l <- lapply(l, function(x) { x[row.names(x) %in% common_names,] })
l <- lapply(l, function(x) x%>% select(symbol,entrezid))
write.xlsx(l[[1]], file="down.3vs12.xlsx", sheetName = "e-3vs12", col.names = TRUE, row.names = TRUE, append = FALSE)

Gene up-regulated in Tissue 1 vs Tissue 2 with the lowest FDR and up-regulated also in Tissue 1 vs Tissue 3.

l <- list(head(up.genes.1vs2), up.genes.1vs3)
common_names = Reduce(intersect, lapply(l, row.names))
l <- lapply(l, function(x) { x[row.names(x) %in% common_names,] })
l <- lapply(l, function(x) x%>% select(symbol,entrezid))
l[[1]][1,]

GO & KEGG pathway analysis

The gene ontology (GO) and the KEGG pathway analysis are the common downstream procedures to interpret the differential expression results in a biological context. Given a set of genes that are up- or down-regulated under a certain contrast of interest, a GO (or pathway) analysis will find which GO terms (or pathways) are over- or under-represented using annotations for the genes in that set. Suppose we want to identify GO terms and KEGG pathways in group 1 (brain) compared to group 2 (pancreas) from the previous analysis.

Focusing on the ontology of Biological Process (BP) and metabolic pathways (given by KEGG) we can observe up-regulated genes that in the tumors (or more in general in diseases, since we have no cancerous cells condition) tend to be associated with cell differentiation, cell migration and tissue morphogenesis.

go <- goana(list(Up=up.genes.1vs2$entrezid, Down=down.genes.1vs2$entrezid), species="Hs", FDR=0.01)
topGO(go, ontology = "BP", number = 30L, truncate.term = NULL, sort="Up")
keg <- kegga(list(Up=up.genes.1vs2$entrezid, Down=down.genes.1vs2$entrezid), species="Hs", FDR=0.01)
topKEGG(keg, number = 30L, truncate.path = NULL, sort="Up")
library(clusterProfiler)
ego <- enrichGO(up.genes.1vs2$entrezid, OrgDb="org.Hs.eg.db", ont="BP", readable=TRUE, pvalueCutoff=0.01)

library(enrichplot)
dotplot(ego, showCategory=30)
wrong orderBy parameter; set to default `orderBy = "x"`

kk <- enrichKEGG(up.genes.1vs2$entrezid, organism = "hsa", pvalueCutoff=0.01)
dotplot(kk, showCategory=30)
wrong orderBy parameter; set to default `orderBy = "x"`

These bunch of genes up-regulated showed above are all associated with the nervous system (probably they are over-expressed in older ages and also in cerebrovascular or neurological diseases, principal causes of death of these donors).

Down-regulated 1vs2 genes are mostly associated with pancreatic functions. We can also appreciate the presence of “Maturity onset diabetes of the young” (KEGG) which refers to any of several hereditary forms of diabetes mellitus caused by mutations in an autosomal dominant gene disrupting insulin production in islets of Langerhans of pancreas.

topGO(go, ontology = "BP", number = 30L, truncate.term = NULL, sort="Down")
topKEGG(keg, number = 30L, truncate.path = NULL, sort="Down")
ego <- enrichGO(down.genes.1vs2$entrezid, OrgDb="org.Hs.eg.db", ont="BP", readable=TRUE, pvalueCutoff=0.01)
dotplot(ego, showCategory=30)
wrong orderBy parameter; set to default `orderBy = "x"`

kk <- enrichKEGG(down.genes.1vs2$entrezid, organism = "hsa", pvalueCutoff=0.01)
dotplot(kk, showCategory=30)
wrong orderBy parameter; set to default `orderBy = "x"`

We can do the same for group 1 (brain) compared to group 3 (liver).

go <- goana(list(Up=up.genes.1vs3$entrezid, Down=down.genes.1vs3$entrezid), species="Hs", FDR=0.01)
topGO(go, ontology = "BP", number = 30L, truncate.term = NULL, sort = "Up")
keg <- kegga(list(Up=up.genes.1vs3$entrezid, Down=down.genes.1vs3$entrezid), species="Hs", FDR=0.01)
topKEGG(keg, number = 30L, truncate.path = NULL, sort="Down")
ego <- enrichGO(up.genes.1vs3$entrezid, OrgDb="org.Hs.eg.db", ont="BP", readable=TRUE, pvalueCutoff=0.01)
dotplot(ego, showCategory=30)
wrong orderBy parameter; set to default `orderBy = "x"`

kk <- enrichKEGG(up.genes.1vs3$entrezid, organism = "hsa", pvalueCutoff=0.01)
dotplot(kk, showCategory=30)
wrong orderBy parameter; set to default `orderBy = "x"`

We can see similar results, up-regulated genes are associated with neural functions. Let’s go for down-regulated genes.

topGO(go, ontology = "BP", number = 30L, truncate.term = NULL, sort="Down")
topKEGG(keg, number = 30L, truncate.path = NULL,sort="Down")
ego <- enrichGO(down.genes.1vs3$entrezid, OrgDb="org.Hs.eg.db", ont="BP", readable=TRUE, pvalueCutoff=0.01)
dotplot(ego, showCategory=15)
wrong orderBy parameter; set to default `orderBy = "x"`

kk <- enrichKEGG(down.genes.1vs3$entrezid, organism = "hsa", pvalueCutoff=0.01)
dotplot(kk, showCategory=30)
wrong orderBy parameter; set to default `orderBy = "x"`

Reaching similar results of 1vs2. Down-regulated genes are associated with hepatic (instead of pancreatic) functions, such as metabolic ones, bile secretion, hormone biosynthesis and so on and so forth.

Finally, group 2 (pancreas) compared to group 3 (liver).

go <- goana(list(Up=up.genes.2vs3$entrezid, Down=down.genes.2vs3$entrezid), species="Hs", FDR=0.01)
topGO(go, ontology = "BP", number = 30L, truncate.term = NULL, sort = "Up")
keg <- kegga(list(Up=up.genes.2vs3$entrezid, Down=down.genes.2vs3$entrezid), species="Hs", FDR=0.01)
topKEGG(keg, number = 30L, truncate.path = NULL, sort = "Up")
ego <- enrichGO(up.genes.2vs3$entrezid, OrgDb="org.Hs.eg.db", ont="BP", readable=TRUE, pvalueCutoff=0.01)
dotplot(ego, showCategory=30)
wrong orderBy parameter; set to default `orderBy = "x"`

kk <- enrichKEGG(up.genes.2vs3$entrezid, organism = "hsa", pvalueCutoff=0.01)
dotplot(kk, showCategory=30)
wrong orderBy parameter; set to default `orderBy = "x"`

Googling information on those bunch of genes (https://www.ebi.ac.uk/QuickGO/) we see that most of these up-regulated genes (and their final products) are associated with metabolic processing taking place in both pancreatic and hepatic cells (we can see both “Pancreatic secretion” and “Gastric acid secretion” for example).

Let’s see down-regulated genes.

topGO(go, ontology = "BP", number = 30L, truncate.term = NULL, sort="Down")
topKEGG(keg, number = 30L, truncate.path = NULL, sort="Down")
ego <- enrichGO(down.genes.2vs3$entrezid, OrgDb="org.Hs.eg.db", ont="BP", readable=TRUE, pvalueCutoff=0.01)
dotplot(ego, showCategory=30)
wrong orderBy parameter; set to default `orderBy = "x"`

kk <- enrichKEGG(down.genes.2vs3$entrezid, organism = "hsa", pvalueCutoff=0.01)
dotplot(kk, showCategory=30)
wrong orderBy parameter; set to default `orderBy = "x"`

We obtained a similar list of down-regulated genes of 1vs3: down-regulated genes are associated with hepatic functions.

The aim of the post-processing bulk RNA-Seq dataset given by this last analysis is achieved. We used two tools (and generated respective clusterProfiler dot plots) in order to determine whether the results “make sense”; the GO annotations & KEGG pathways are consistent with the fact that the genes are up-regulated or down-regulated in our three tissues. In other words, if the samples were “anonymous”, we’d have been able to discover from which tissue each sample was taken from.

sessionInfo()

This analysis was conducted on:

sessionInfo()
R version 4.0.4 (2021-02-15)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Big Sur 10.16

Matrix products: default
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib

locale:
[1] it_IT.UTF-8/it_IT.UTF-8/it_IT.UTF-8/C/it_IT.UTF-8/it_IT.UTF-8

attached base packages:
[1] parallel  stats4    stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] enrichplot_1.10.2           clusterProfiler_3.18.1      dplyr_1.0.7                 xlsx_0.6.5                  org.Hs.eg.db_3.12.0        
 [6] AnnotationDbi_1.52.0        statmod_1.4.36              recount_1.16.1              SummarizedExperiment_1.20.0 Biobase_2.50.0             
[11] GenomicRanges_1.42.0        GenomeInfoDb_1.26.7         IRanges_2.24.1              S4Vectors_0.28.1            BiocGenerics_0.36.1        
[16] MatrixGenerics_1.2.1        matrixStats_0.59.0          edgeR_3.32.1                limma_3.46.0               

loaded via a namespace (and not attached):
  [1] rappdirs_0.3.3           rtracklayer_1.50.0       scattermore_0.7          SeuratObject_4.0.2       tidyr_1.1.3             
  [6] bumphunter_1.32.0        ggplot2_3.3.5            bit64_4.0.5              knitr_1.33               irlba_2.3.3             
 [11] DelayedArray_0.16.3      data.table_1.14.0        rpart_4.1-15             RCurl_1.98-1.3           GEOquery_2.58.0         
 [16] derfinder_1.24.2         generics_0.1.0           GenomicFeatures_1.42.3   cowplot_1.1.1            RSQLite_2.2.7           
 [21] shadowtext_0.0.8         RANN_2.6.1               future_1.21.0            bit_4.0.4                spatstat.data_2.1-0     
 [26] xml2_1.3.2               httpuv_1.6.1             assertthat_0.2.1         viridis_0.6.1            xfun_0.24               
 [31] hms_1.1.0                rJava_1.0-4              evaluate_0.14            promises_1.2.0.1         fansi_0.5.0             
 [36] progress_1.2.2           dbplyr_2.1.1             igraph_1.2.6             DBI_1.1.1                htmlwidgets_1.5.3       
 [41] spatstat.geom_2.2-0      purrr_0.3.4              ellipsis_0.3.2           backports_1.2.1          biomaRt_2.46.3          
 [46] deldir_0.2-10            vctrs_0.3.8              ROCR_1.0-11              abind_1.4-5              cachem_1.0.5            
 [51] ggforce_0.3.3            BSgenome_1.58.0          checkmate_2.0.0          sctransform_0.3.2        GenomicAlignments_1.26.0
 [56] prettyunits_1.1.1        goftest_1.2-2            cluster_2.1.2            DOSE_3.16.0              lazyeval_0.2.2          
 [61] crayon_1.4.1             labeling_0.4.2           pkgconfig_2.0.3          tweenr_1.0.2             nlme_3.1-152            
 [66] nnet_7.3-16              rlang_0.4.11             globals_0.14.0           lifecycle_1.0.0          miniUI_0.1.1.1          
 [71] downloader_0.4           BiocFileCache_1.14.0     polyclip_1.10-0          lmtest_0.9-38            rngtools_1.5            
 [76] Matrix_1.3-4             zoo_1.8-9                base64enc_0.1-3          ggridges_0.5.3           png_0.1-7               
 [81] viridisLite_0.4.0        bitops_1.0-7             KernSmooth_2.23-20       Biostrings_2.58.0        blob_1.2.1              
 [86] doRNG_1.8.2              stringr_1.4.0            qvalue_2.22.0            parallelly_1.26.1        readr_1.4.0             
 [91] jpeg_0.1-8.1             scales_1.1.1             memoise_2.0.0            magrittr_2.0.1           plyr_1.8.6              
 [96] ica_1.0-2                derfinderHelper_1.24.1   zlibbioc_1.36.0          compiler_4.0.4           scatterpie_0.1.6        
[101] RColorBrewer_1.1-2       fitdistrplus_1.1-5       Rsamtools_2.6.0          cli_3.0.0                XVector_0.30.0          
[106] listenv_0.8.0            patchwork_1.1.1          pbapply_1.4-3            htmlTable_2.2.1          Formula_1.2-4           
[111] MASS_7.3-54              mgcv_1.8-36              tidyselect_1.1.1         stringi_1.6.2            yaml_2.2.1              
[116] GOSemSim_2.16.1          askpass_1.1              locfit_1.5-9.4           latticeExtra_0.6-29      ggrepel_0.9.1           
[121] grid_4.0.4               VariantAnnotation_1.36.0 fastmatch_1.1-0          tools_4.0.4              future.apply_1.7.0      
[126] rstudioapi_0.13          foreach_1.5.1            foreign_0.8-81           gridExtra_2.3            farver_2.1.0            
[131] Rtsne_0.15               ggraph_2.0.5             digest_0.6.27            rvcheck_0.1.8            BiocManager_1.30.16     
[136] shiny_1.6.0              Rcpp_1.0.6               later_1.2.0              RcppAnnoy_0.0.18         httr_1.4.2              
[141] colorspace_2.0-2         XML_3.99-0.6             tensor_1.5               reticulate_1.20          splines_4.0.4           
[146] uwot_0.1.10              spatstat.utils_2.2-0     graphlayouts_0.7.1       xlsxjars_0.6.1           plotly_4.9.4.1          
[151] xtable_1.8-4             jsonlite_1.7.2           GenomicFiles_1.26.0      tidygraph_1.2.0          R6_2.5.0                
[156] Hmisc_4.5-0              pillar_1.6.1             htmltools_0.5.1.1        mime_0.11                glue_1.4.2              
[161] fastmap_1.1.0            BiocParallel_1.24.1      codetools_0.2-18         fgsea_1.16.0             utf8_1.2.1              
[166] lattice_0.20-44          spatstat.sparse_2.0-0    tibble_3.1.2             curl_4.3.2               rentrez_1.2.3           
[171] leiden_0.3.8             GO.db_3.12.1             openssl_1.4.4            survival_3.2-11          rmarkdown_2.9           
[176] munsell_0.5.0            DO.db_2.9                GenomeInfoDbData_1.2.4   iterators_1.0.13         reshape2_1.4.4          
[181] gtable_0.3.0             spatstat.core_2.2-0      Seurat_4.0.3            

Bibliography

Bioconductor; recount quick start guide & workflow; https://bioconductor.org/packages/release/bioc/vignettes/recount/inst/doc/recount-quickstart.html & https://bioconductor.org/packages/release/workflows/vignettes/recountWorkflow/inst/doc/recount-workflow.html

Giulio Pavesi; DE gene analysis with edgeR (2021 Update); http://159.149.160.56/Transcriptomics/edgeR_exercise.html

Bioconductor; Empirical Analysis of Digital Gene Expression Data in R; https://bioconductor.org/packages/release/bioc/html/edgeR.html

Giulio Pavesi; Annotations Exercise; http://159.149.160.56/Transcriptomics/gata6.html

Bioconductor; clusterProfiler; https://bioconductor.org/packages/release/bioc/html/clusterProfiler.html

Mohammed Khalfan; Gene Set Enrichment Analysis with ClusterProfiler; https://learn.gencore.bio.nyu.edu/rna-seq-analysis/gene-set-enrichment-analysis/

LS0tCnRpdGxlOiAiQnVsayBSTkEtU2VxIFRyYW5zY3JpcHRvbWljcyBwcm9qZWN0IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKYXV0aG9yOiBFbWFudWVsZSBDYXZhbGxlcmkgOTg1ODg4IChleCA5MDg4MzgsIEkgdXNlZCB0aGF0IG9uZSkKLS0tCgpUaGlzIGlzIHRoZSBSIHZpZ25ldHRlIG9mIHRoZSBCdWxrIFJOQS1TZXEgVHJhbnNjcmlwdG9taWNzIHByb2plY3QuIEluIHRoaXMgdmlnbmV0dGUgd2Ugd2lsbCBzdGFydCBmcm9tIGEgUmVjb3VudCB0YWJsZSwgd2hvc2UgY291bnRzIGRlcml2ZSBmcm9tIFJOQS1zZXEgc2FtcGxlcyBvZiBodW1hbiBicmFpbiBjZWxscyBjb21wYXJlZCB0byBwYW5jcmVhcyBhbmQgbGl2ZXIgY2VsbHMgKHNlZSBodHRwOi8vMTU5LjE0OS4xNjAuNTYvR1RfZmlsZXMvSnVseTMwLyBhbmQgYWxzbyBodHRwczovL2d0ZXhwb3J0YWwub3JnL2hvbWUvKS4KCiMgUHJlLXByb2Nlc3Npbmcgc3RlcHMKCkRhdGFzZXRzIGFyZSBpbiB0aGUg4oCcUmFuZ2VkIFN1bW1hcml6ZWQgRXhwZXJpbWVudOKAnSBmb3JtYXQgb2YgUmVjb3VudDIuCmBgYHtyLCByZXN1bHRzPSdoaWRlJ30KbGlicmFyeSgicmVjb3VudCIsIHF1aWV0bHkgPSBUUlVFKQpgYGAKCkxldOKAmXMgc3RhcnQgZnJvbSB0aGUgZmlyc3QgdGlzc3VlIG1lbnRpb25lZDogYnJhaW4uIFdlIGNhbiBsb2FkIGVhY2ggZGF0YXNldCB3aXRoIHRoZSBsb2FkKCkgY29tbWFuZC4KYGBge3J9CmRhdGEgPC0gbG9hZCgicnNlX2dlbmVfYnJhaW5fOV9zY2FsZWQuUmRhdGEiKQpkYXRhCmBgYAoKVGhlIG9iamVjdCBjb250YWluaW5nIHRoZSBkYXRhIGlzIGNhbGxlZCAicnNlIiBhbmQgaXRzIHR5cGUgaXMgIlJhbmdlZFN1bW1hcml6ZWRFeHBlcmltZW50Ii4KYGBge3J9CnJzZQpgYGAKCkNvdW50cyBoYXZlIGFscmVhZHkgYmVlbiBub3JtYWxpemVkL3NjYWxlZCAodG8gNDBNIHJlYWRzIHBlciBjb2x1bW4pLCB0aGVyZWZvcmUgdGhleSBkbyBub3QgbmVlZDoKYGBge3J9CiNyc2UgPC0gc2NhbGVfY291bnRzKHJzZSkKYGBgCgpBbGwgcm93IGRhdGEgYXJlIGlkZW50aWNhbCwgdGhleSByZXByZXNlbnQgdGhlIEdlbmNvZGUgVjI1IGNvbXByZWhlbnNpdmUgYW5ub3RhdGlvbi4KCldlIGNhbiBleHBsb3JlIHRoZSBpbmZvcm1hdGlvbiBhc3NvY2lhdGVkIHdpdGggdGhlIHNhbXBsZXMgd2l0aCBjb2xEYXRhLCBhbmQgd2l0aCByb3dzIHdpdGggcm93RGF0YS4KYGBge3J9CmhlYWQoY29sRGF0YShyc2UpLCAyKQpgYGAKCmBgYHtyfQpyb3dEYXRhKHJzZSkKYGBgCgpCdXQgYWxzbyB0aGUgZ2Vub21pYyBjb29yZGluYXRlcyBvZiBnZW5lcy4KYGBge3J9CnJvd1Jhbmdlcyhyc2UpCmBgYAoKU2VsZWN0IHRoZSB0aHJlZSBjb2x1bW5zIHdlIGhhdmUgdG8gd29yayBvbjogOCAtPiA4IDkgMTAuIFdoYXQgYXJlIHRoZWlyIG5hbWVzPwpgYGB7cn0Kcm93bmFtZXMoY29sRGF0YShyc2UpW2NvbG5hbWVzKHJzZSlbODoxMF0sXSkKYGBgCgpUb3RhbCBudW1iZXIgb2YgcmVhZHMgcGVyIGNvbHVtbjoKYGBge3J9CmMoc3VtKGFzc2F5cyhyc2UpJGNvdW50c1ssOF0pLCBzdW0oYXNzYXlzKHJzZSkkY291bnRzWyw5XSksIHN1bShhc3NheXMocnNlKSRjb3VudHNbLDEwXSkpCmBgYAoKUmVtb3ZlIGFsbCBnZW5lcyB3aXRoIGxlbmd0aCA8IDIwMCwgb3IsIGluIG90aGVyIHdvcmRzLCBrZWVwIGFsbCBnZW5lcyB3aXRoIGxlbmd0aCA+PSAyMDAuLi4KYGBge3J9CnJzZSA8LSByc2VbLCA4OjEwXSAJCQpyc2UgPC0gcnNlW3Jvd0RhdGEocnNlKSRicF9sZW5ndGggPj0gMjAwLF0gCmBgYAoKYW5kIHByb2R1Y2UgdGhlIG92ZXJhbGwgcmVhZCBjb3VudCBhc3NvY2lhdGVkIHdpdGggdGhlc2UgZ2VuZXMgaW4gZWFjaCB0aXNzdWUvcmVwbGljYXRlLiBMZXQgdXMgZXh0cmFjdCB0aGUgY291bnQgbWF0cml4IGZyb20gdGhlIHJzZSBvYmplY3QuCmBgYHtyfQpsb25nZXJUaGFuMjAwIDwtIGFzc2F5cyhyc2UpJGNvdW50cwpoZWFkKGxvbmdlclRoYW4yMDApCmBgYAoKVG90YWwgbnVtYmVyIG9mIHJlYWRzIHRpc3N1ZSBwZXIgY29sdW1uIGFmdGVyIHJlbW92YWw6CmBgYHtyfQpjKHN1bShsb25nZXJUaGFuMjAwWywxXSksIHN1bShsb25nZXJUaGFuMjAwWywyXSksIHN1bShsb25nZXJUaGFuMjAwWywzXSkpCmBgYAoKQmVmb3JlIHJlbW92aW5nIGFsbCBtaXRvY2hvbmRyaWFsIGdlbmVzIHdlIGhhdmUgdG8gY2hlY2sgaWYgdGhlc2Ugb25lcyBiZWdpbiB3aXRoIE1ULgpgYGB7cn0KdW5saXN0KHJvd0RhdGEocnNlKSRzeW1ib2wsIHVzZS5uYW1lcyA9IEZBTFNFKVtncmVwKCJeTVQtKiIsIHVubGlzdChyb3dEYXRhKHJzZSkkc3ltYm9sLCB1c2UubmFtZXMgPSBGQUxTRSkpXSAKYGBgCgpUaGVuIHdlIGNhbiBrZWVwICEobWl0b2Nob25kcmlhbCBnZW5lcykuLi4KYGBge3J9Cm10IDwtIHJvd0RhdGEocnNlKSRnZW5lX2lkW2dyZXAoIl5NVC0qIiwgdW5saXN0KHJvd0RhdGEocnNlKSRzeW1ib2wsIHVzZS5uYW1lcyA9IEZBTFNFKSldCmBgYAoKYW5kIHByb2R1Y2UgdGhlIG92ZXJhbGwgcmVhZCBjb3VudCBhc3NvY2lhdGVkIHdpdGggdGhlc2UgZ2VuZXMgaW4gZWFjaCB0aXNzdWUvcmVwbGljYXRlLgpgYGB7cn0KZmlsdGVyZWQxIDwtIGFzc2F5cyhyc2UpJGNvdW50cwpmaWx0ZXJlZDEgPC0gZmlsdGVyZWQxW3doaWNoKCEocm93bmFtZXMoZmlsdGVyZWQxKSAlaW4lIG10KSksXQpoZWFkKGZpbHRlcmVkMSkKYGBgCgpUb3RhbCBudW1iZXIgb2YgcmVhZHMgdGlzc3VlIHBlciBjb2x1bW4gYWZ0ZXIgcmVtb3ZhbDoKYGBge3J9CmMoc3VtKGZpbHRlcmVkMVssMV0pLCBzdW0oZmlsdGVyZWQxWywyXSksIHN1bShmaWx0ZXJlZDFbLDNdKSkKYGBgCgpHRU8gaW5mb3JtYXRpb24gd2FzIGFic2VudCBmb3IgdGhlIFNSUDAxMjY4MiBkYXRhc2V0LgpgYGB7cn0KY29sRGF0YShyc2UpWywgYygiZ2VvX2FjY2Vzc2lvbiIsICJ0aXRsZSIsICJjaGFyYWN0ZXJpc3RpY3MiKV0KYGBgCgpXZSBjYW4gZXhwYW5kIHRoZSBiaW9sb2dpY2FsIG1ldGFkYXRhIGluZm9ybWF0aW9uIGJ5IGFkZGluZyBwcmVkaWN0aW9ucyBiYXNlZCBvbiBSTkEtc2VxIGRhdGEuIFRoZSBwcmVkaWN0aW9ucyBpbmNsdWRlIGluZm9ybWF0aW9uIGFib3V0IHNleCwgc2FtcGxlIHNvdXJjZSAoY2VsbCBsaW5lIHZzIHRpc3N1ZSksIHRpc3N1ZSBhbmQgdGhlIHNlcXVlbmNpbmcgc3RyYXRlZ3kgdXNlZC4KYGBge3J9CnJzZSA8LSBhZGRfcHJlZGljdGlvbnMocnNlKQpgYGAKCmBgYHtyfQpjb2xEYXRhKHJzZSlbLCA4MzpuY29sKGNvbERhdGEocnNlKSldCmBgYAoKUHJvamVjdCBTUlAwMTI2ODIgaGFzIGEgZmV3IGV4dHJhIGJpb2xvZ2ljYWxseSByZWxldmFudCB2YXJpYWJsZXMgdmlhIHRoZSBTUkEgUnVuIHNlbGVjdG9yIGh0dHBzOi8vdHJhY2UubmNiaS5ubG0ubmloLmdvdi9UcmFjZXMvc3R1ZHkvP2FjYz1TUlAwMTI2ODIuCmBgYHtyfQpzcmEgPC0gcmVhZC5jc3YoIlNyYVJ1blRhYmxlLnR4dCIsIGhlYWRlciA9IFRSVUUpCgojIEV4cGxvcmUgaXQKaGVhZChzcmEpCmBgYApgYGB7cn0KIyBTZXQgYWxsIGNvbHVtbiBuYW1lcyBpbiBsb3dlciBjYXNlCmNvbG5hbWVzKHNyYSkgPC0gdG9sb3dlcihjb2xuYW1lcyhzcmEpKQoKIyBDaG9vc2Ugc29tZSBtZWFuaW5nZnVsIHZhcmlhYmxlcyB3ZSB3YW50IHRvIGFkZApzcmFfdmFycyA8LSBjKAogICAgInNleCIsICJib2R5X3NpdGUiLCAiaGlzdG9sb2dpY2FsX3R5cGUiLCAiaXNfdHVtb3IiLCAic3VibWl0dGVkX3N1YmplY3RfaWQiCikKc3RvcGlmbm90KGFsbChzcmFfdmFycyAlaW4lIGNvbG5hbWVzKHNyYSkpKQoKIyBSZS1vcmdhbml6ZSB0aGUgU1JBIHRhYmxlIGJhc2VkIG9uIHRoZSBTUkEgUnVuIElEcyB3ZSBoYXZlCnNyYTEgPC0gc3JhW21hdGNoKGNvbERhdGEocnNlKSRydW4sIHNyYSRydW4pLCBdCgojIERvdWJsZSBjaGVjayB0aGUgb3JkZXIKc3RvcGlmbm90KGlkZW50aWNhbChjb2xEYXRhKHJzZSkkcnVuLCBhcy5jaGFyYWN0ZXIoc3JhMSRydW4pKSkKCiMgQXBwZW5kIHRoZSB2YXJpYWJsZXMgb2YgaW50ZXJlc3QKY29sRGF0YShyc2UpIDwtIGNiaW5kKGNvbERhdGEocnNlKSwgc3JhMVssIHNyYV92YXJzXSkKCmNvbERhdGEocnNlKVssIDk1Om5jb2woY29sRGF0YShyc2UpKV0KYGBgCgpTYW1lIHN0ZXBzIGZvciB0aGUgb3RoZXIgdHdvIHRpc3N1ZXMuCmBgYHtyfQpkYXRhIDwtIGxvYWQoInJzZV9nZW5lX3BhbmNyZWFzXzBfc2NhbGVkLlJkYXRhIikKcm93bmFtZXMoY29sRGF0YShyc2UpW2NvbG5hbWVzKHJzZSlbMzo1XSxdKQpjKHN1bShhc3NheXMocnNlKSRjb3VudHNbLDNdKSwgc3VtKGFzc2F5cyhyc2UpJGNvdW50c1ssNF0pLCBzdW0oYXNzYXlzKHJzZSkkY291bnRzWyw1XSkpCnJzZSA8LSByc2VbLCAzOjVdCSMgMyAtPiAzIDQgNS4KcnNlIDwtIHJzZVtyb3dEYXRhKHJzZSkkYnBfbGVuZ3RoID49IDIwMCxdIApsb25nZXJUaGFuMjAwIDwtIGFzc2F5cyhyc2UpJGNvdW50cwpoZWFkKGxvbmdlclRoYW4yMDApCmMoc3VtKGxvbmdlclRoYW4yMDBbLDFdKSwgc3VtKGxvbmdlclRoYW4yMDBbLDJdKSwgc3VtKGxvbmdlclRoYW4yMDBbLDNdKSkKI0NoZWNrIGlmIG1pdG9jaG9uZHJpYWwgZ2VuZXMgYmVnaW4gd2l0aCBNVC4KdW5saXN0KHJvd0RhdGEocnNlKSRzeW1ib2wsIHVzZS5uYW1lcyA9IEZBTFNFKVtncmVwKCJeTVQtKiIsIHVubGlzdChyb3dEYXRhKHJzZSkkc3ltYm9sLCB1c2UubmFtZXMgPSBGQUxTRSkpXSAKbXQgPC0gcm93RGF0YShyc2UpJGdlbmVfaWRbZ3JlcCgiXk1ULSoiLCB1bmxpc3Qocm93RGF0YShyc2UpJHN5bWJvbCwgdXNlLm5hbWVzID0gRkFMU0UpKV0KZmlsdGVyZWQyIDwtIGFzc2F5cyhyc2UpJGNvdW50cwpmaWx0ZXJlZDIgPC0gZmlsdGVyZWQyW3doaWNoKCEocm93bmFtZXMoZmlsdGVyZWQyKSAlaW4lIG10KSksXQpoZWFkKGZpbHRlcmVkMikKYyhzdW0oZmlsdGVyZWQyWywxXSksIHN1bShmaWx0ZXJlZDJbLDJdKSwgc3VtKGZpbHRlcmVkMlssM10pKQpgYGAKCkNvbHVtbnMgY29udGFpbiBhIGxvdCBvZiBpbmZvcm1hdGlvbiwgdGhhdCBwZXJtaXQgdG8gbG9vayB1cCBlYWNoIHNhbXBsZSBpbiB0aGUgR1RFeCBkYXRhYmFzZSBmb3IgZnVydGhlciBkZXRhaWxzLCBlLmcuIGFnZS9zZXggb2YgdGhlIGRvbm9yLCBhIHBpY3R1cmUgb2YgdGhlIHRpc3N1ZSBzYW1wbGUgaXRzZWxmLCBhbmQgc28gb24gKGh0dHBzOi8vZ3RleHBvcnRhbC5vcmcvaG9tZS9oaXN0b2xvZ3lQYWdlKS4gVW5mb3J0dW5hdGVseSB3ZSBjb3VsZCBub3QgYXBwcmVjaWF0ZSB0aG9zZSBpbmZvcm1hdGlvbiBiZWZvcmUgYmVjYXVzZSAic2FtcGlkInMgb2Ygb3VyIHRocmVlIGJyYWluIHJlcGxpY2F0ZXMgYXJlIG5vdCBwcmVzZW50IGluIHRoZSBHVEV4IGRhdGFiYXNlLgpgYGB7cn0KY29sRGF0YShyc2UpJHNhbXBpZApgYGAKCkFsbCBzYW1wbGVzIHdlcmUgY29sbGVjdGVkIGZyb20gbWFsZXMgYmV0d2VlbiA1MCBhbmQgNjkgeW8gYWxsIGRpZWQgZnJvbSAidmVudGlsYXRvciBjYXNlIiAoSGFyZHkgc2NhbGUpIGFuZCBvbmx5IHRoZSBmaXJzdCBoYWQgYSBmaWJyb3Npcy4KYGBge3J9CmNvbERhdGEocnNlKSRzbXB0aG50cwpgYGAKCmBgYHtyfQpyc2UgPC0gYWRkX3ByZWRpY3Rpb25zKHJzZSkKY29sRGF0YShyc2UpWywgODM6bmNvbChjb2xEYXRhKHJzZSkpXQpgYGAKCmBgYHtyfQpzcmEyIDwtIHNyYVttYXRjaChjb2xEYXRhKHJzZSkkcnVuLCBzcmEkcnVuKSwgXQoKIyBEb3VibGUgY2hlY2sgdGhlIG9yZGVyCnN0b3BpZm5vdChpZGVudGljYWwoY29sRGF0YShyc2UpJHJ1biwgYXMuY2hhcmFjdGVyKHNyYTIkcnVuKSkpCgojIEFwcGVuZCB0aGUgdmFyaWFibGVzIG9mIGludGVyZXN0CmNvbERhdGEocnNlKSA8LSBjYmluZChjb2xEYXRhKHJzZSksIHNyYTJbLCBzcmFfdmFyc10pCgpjb2xEYXRhKHJzZSlbLCA5NTpuY29sKGNvbERhdGEocnNlKSldCmBgYAoKYGBge3J9CmRhdGEgPC0gbG9hZCgicnNlX2dlbmVfbGl2ZXJfOF9zY2FsZWQuUmRhdGEiKQpyb3duYW1lcyhjb2xEYXRhKHJzZSlbY29sbmFtZXMocnNlKVs4OjEwXSxdKQpjKHN1bShhc3NheXMocnNlKSRjb3VudHNbLDhdKSwgc3VtKGFzc2F5cyhyc2UpJGNvdW50c1ssOV0pLCBzdW0oYXNzYXlzKHJzZSkkY291bnRzWywxMF0pKQpyc2UgPC0gcnNlWywgODoxMF0JIyA4IC0+IDggOSAxMC4KcnNlIDwtIHJzZVtyb3dEYXRhKHJzZSkkYnBfbGVuZ3RoID49IDIwMCxdIApsb25nZXJUaGFuMjAwIDwtIGFzc2F5cyhyc2UpJGNvdW50cwpoZWFkKGxvbmdlclRoYW4yMDApCmMoc3VtKGxvbmdlclRoYW4yMDBbLDFdKSwgc3VtKGxvbmdlclRoYW4yMDBbLDJdKSwgc3VtKGxvbmdlclRoYW4yMDBbLDNdKSkKI0NoZWNrIGlmIG1pdG9jaG9uZHJpYWwgZ2VuZXMgYmVnaW4gd2l0aCBNVC4KdW5saXN0KHJvd0RhdGEocnNlKSRzeW1ib2wsIHVzZS5uYW1lcyA9IEZBTFNFKVtncmVwKCJeTVQtKiIsIHVubGlzdChyb3dEYXRhKHJzZSkkc3ltYm9sLCB1c2UubmFtZXMgPSBGQUxTRSkpXSAKbXQgPC0gcm93RGF0YShyc2UpJGdlbmVfaWRbZ3JlcCgiXk1ULSoiLCB1bmxpc3Qocm93RGF0YShyc2UpJHN5bWJvbCwgdXNlLm5hbWVzID0gRkFMU0UpKV0KZmlsdGVyZWQzIDwtIGFzc2F5cyhyc2UpJGNvdW50cwpmaWx0ZXJlZDMgPC0gZmlsdGVyZWQzW3doaWNoKCEocm93bmFtZXMoZmlsdGVyZWQzKSAlaW4lIG10KSksXQpoZWFkKGZpbHRlcmVkMykKYyhzdW0oZmlsdGVyZWQzWywxXSksIHN1bShmaWx0ZXJlZDNbLDJdKSwgc3VtKGZpbHRlcmVkM1ssM10pKQpgYGAKCmBgYHtyfQpjb2xEYXRhKHJzZSkkc2FtcGlkCmBgYApUaGUgZmlyc3QgcmVwbGljYXRlIHdhcyBjb2xsZWN0ZWQgZnJvbSBhIG1hbiAoNDAtNDkgeW8pIGFuZCBpdCdzIGFzc29jaWF0ZWQgd2l0aCAibmVjcm9zaXMiLCBtYW4gZGllZCBmcm9tIHZlbnRpbGF0b3IgY2FzZS4gU2Vjb25kIG9uZSBjb21lcyBmcm9tIGEgd29tYW4gKDYwLTY5IHlvKSBkaWVkIGZyb20gbmF0dXJhbCBjYXVzZXMgYW5kIHRoZSB0aXNzdWUgc2hvd3MgY29uZ2VzdGlvbiBhbmQgc3RlYXRvc2lzLiBMYXN0IG9uZSBjb21lcyBmcm9tIGEgd29tYW4gZGllZCBmcm9tIHZlbnRpbGF0b3IgY2FzZSAoNDAtNDkgeW8pIGFuZCB0aGUgdGlzc3VlIHNob3dzIG5vIHBhdGhvbG9neS4KCmBgYHtyfQpjb2xEYXRhKHJzZSkkc21wdGhudHMKYGBgCgpgYGB7cn0KcnNlIDwtIGFkZF9wcmVkaWN0aW9ucyhyc2UpCmNvbERhdGEocnNlKVssIDgzOm5jb2woY29sRGF0YShyc2UpKV0KYGBgCgpgYGB7cn0KIyBSZS1vcmdhbml6ZSB0aGUgU1JBIHRhYmxlIGJhc2VkIG9uIHRoZSBTUkEgUnVuIElEcyB3ZSBoYXZlCnNyYTMgPC0gc3JhW21hdGNoKGNvbERhdGEocnNlKSRydW4sIHNyYSRydW4pLCBdCgojIERvdWJsZSBjaGVjayB0aGUgb3JkZXIKc3RvcGlmbm90KGlkZW50aWNhbChjb2xEYXRhKHJzZSkkcnVuLCBhcy5jaGFyYWN0ZXIoc3JhMyRydW4pKSkKCiMgQXBwZW5kIHRoZSB2YXJpYWJsZXMgb2YgaW50ZXJlc3QKY29sRGF0YShyc2UpIDwtIGNiaW5kKGNvbERhdGEocnNlKSwgc3JhM1ssIHNyYV92YXJzXSkKCmNvbERhdGEocnNlKVssIDk1Om5jb2woY29sRGF0YShyc2UpKV0KYGBgCgpNZXJnZSB0aGVtIGludG8gYSBzaW5nbGUgY291bnQgdGFibGUvb2JqZWN0LCBmb3Igc3Vic2VxdWVudCBhbmFseXNlcy4KYGBge3J9CmNvdW50VGFibGUgPC0gbWVyZ2UoZmlsdGVyZWQxLCBmaWx0ZXJlZDIsIGJ5ID0gInJvdy5uYW1lcyIsIGFsbCA9IFRSVUUpCnJvd25hbWVzKGNvdW50VGFibGUpIDwtIGNvdW50VGFibGUkUm93Lm5hbWVzCmNvdW50VGFibGUkUm93Lm5hbWVzIDwtIE5VTEwKY291bnRUYWJsZSA8LSBkYXRhLm1hdHJpeChjb3VudFRhYmxlLCByb3duYW1lcy5mb3JjZSA9IE5BKQpjb3VudFRhYmxlIDwtIG1lcmdlKGNvdW50VGFibGUsIGZpbHRlcmVkMywgYnkgPSAicm93Lm5hbWVzIiwgYWxsID0gVFJVRSkKcm93bmFtZXMoY291bnRUYWJsZSkgPC0gY291bnRUYWJsZSRSb3cubmFtZXMKY291bnRUYWJsZSRSb3cubmFtZXMgPC0gTlVMTApjb3VudFRhYmxlIDwtIGRhdGEubWF0cml4KGNvdW50VGFibGUsIHJvd25hbWVzLmZvcmNlID0gTkEpCmhlYWQoY291bnRUYWJsZSkKYGBgCgpMZXQncyBwcmVwYXJlIGEgdGFibGUgc3VtbWFyaXppbmcgd2hhdCB3ZeKAmXZlIGRvbmUsIG9uZSByb3cgcGVyIHRpc3N1ZS9zYW1wbGUuIApgYGB7cn0KYSA8LSBjKCJCcmFpbiAvIFNSUjEzMzc5MDkiLCAiQnJhaW4gLyBTUlIxMzk3MDk0IiwgIkJyYWluIC8gU1JSODE3Njg2IiwgIlBhbmNyZWFzIC8gU1JSMTM3NDczOSIsICJQYW5jcmVhcyAvIFNSUjEwOTc4ODMiLCAiUGFuY3JlYXMgLyBTUlIyMTY3MjA5IiwgIkxpdmVyIC8gU1JSMTM0OTQ3OSIsICJMaXZlciAvIFNSUjE0MzEyNDgiLCAiTGl2ZXIgLyBTUlIxNDA1MDU0IikKYiA8LSBjKDM4MjgwOTAxLCAzNjYyNTQzMywgMzc5NDY1MzgsIDM3OTQ3Njk4LCAzNzI0ODExMywgMzYzMDE3MzcsIDQwOTkwMTI4LCAzNzgyMjE5MCwgMzczNjQ4NzApCmMgPC0gYygxMDY4NzUsIDY5NzMyLCA1ODE4NiwgNjEzOTMsIDY0MDU1LCA4MDc4OCwgNTcwNzIsIDcwNzI2LCA0NjU0NikKZCA8LSBjKDAuMjc5MTg2MjE4LCAwLjE5MDM5MjI4OCwgMC4xNTMzMzY3ODEsIDAuMTYxNzgzMTk5LCAwLjE3MTk2ODQ0MywgMC4yMjI1NDU4MjQsIDAuMTM5MjMzNTI0LCAwLjE4Njk5NjA0NiwgMC4xMjQ1NzE1NTYpCmUgPC0gYyg1OTA3OSwgNzIzOTMsIDkwNjk4LCA1MzI0MCwgNTE3NDEsIDQ2MzU3LCA4NDAxMCwgOTQ4MTIsIDExMzY5MykKZiA8LSBjKDAuMTU0MzMwMjIzLCAwLjE5NzY1NzczMSwgMC4yMzkwMTUyMTYsIDAuMTQwMjk4MzY1LCAwLjEzODkwOTA1LCAwLjEyNzY5OTEyMywgMC4yMDQ5NTE3ODcsIDAuMjUwNjc4MjM5LCAwLjMwNDI3Nzc4OCkKZyA8LSBjKHN1bShjb3VudFRhYmxlWywxXSksIHN1bShjb3VudFRhYmxlWywyXSksIHN1bShjb3VudFRhYmxlWywzXSksIHN1bShjb3VudFRhYmxlWyw0XSksIHN1bShjb3VudFRhYmxlWyw1XSksIHN1bShjb3VudFRhYmxlWyw2XSksIHN1bShjb3VudFRhYmxlWyw3XSksIHN1bShjb3VudFRhYmxlWyw4XSksIHN1bShjb3VudFRhYmxlWyw5XSkpCnRpc3N1ZVBlckNvbHVtbiA8LSBkYXRhLmZyYW1lKFRpc3N1ZUNvbHVtbiA9IGEsIFRvdGFsTnVtYmVyT2ZSZWFkcyA9IGIsIFRvdGFsTnVtYmVyT2ZSZWFkc09uU2hvcnRSTkFzID0gYywgUGVyY2VudGFnZU9mUmVhZHNPblNob3J0Uk5BcyA9IGQsIFRvdGFsTnVtYmVyT2ZSZWFkc09uTVRHZW5lcyA9IGUsIFBlcmNlbnRhZ2VPZlJlYWRzT25NVGdlbmVzID0gZiwgVG90YWxOdW1iZXJPZkZpbHRlcmVkUmVhZHMgPSBnKQpyb3duYW1lcyh0aXNzdWVQZXJDb2x1bW4pIDwtIHRpc3N1ZVBlckNvbHVtbiRUaXNzdWVDb2x1bW4KdGlzc3VlUGVyQ29sdW1uJFRpc3N1ZUNvbHVtbiA8LSBOVUxMCnRpc3N1ZVBlckNvbHVtbgpgYGAKCiMgREUgZ2VuZXMgdXNpbmcgZWRnZVIKClRoZSB0b29sIGZvciBjYWxsaW5nIERFIGdlbmVzIHdlIGNob29zZSBpcyBlZGdlUi4KCmBgYHtyfQpsaWJyYXJ5KCJlZGdlUiIpCmBgYAoKV2UnbGwgcHJvY2VzcyBhbGwgdGlzc3VlcyB0b2dldGhlciwgd2l0aCBhIHN1aXRhYmxlIGRlc2lnbiBtYXRyaXguIFRoZSByb3cgbmFtZXMgb2YgdGhlIHRhYmxlIGFyZSB0aGUgImFubm95aW5nIiBFTlNHIElEcyB3aXRoIHRoZSBudW1iZXIgYXQgdGhlIGVuZCwgc28gd2UgY2xlYW4gdGhlbS4KYGBge3J9CmVuc2VtYmwgPC0gZ3N1YigiXFwuLioiLCAiIiwgcm93bmFtZXMoY291bnRUYWJsZSkpCnJvd25hbWVzKGNvdW50VGFibGUpIDwtIGVuc2VtYmwKaGVhZChjb3VudFRhYmxlKQpgYGAKCldlIGtub3cgdGhhdCB0aGUgZmlyc3QgdGhyZWUgY29sdW1ucyBhcmUgYnJhaW4gY2VsbHMsIHRoZSBzdWJzZXF1ZW50IHRocmVlIGFyZSBwYW5jcmVhcyBjZWxscywgd2hpbGUgdGhlIHJlbWFpbmluZyB0aHJlZSBhcmUgbGl2ZXIgY2VsbHMuIFdlIGdpdmUgdG8gZWRnZVIgdGhlIGNvcnJlc3BvbmRpbmcgaW5mby4KYGBge3J9Cmdyb3VwIDwtIGFzLmZhY3RvcihyZXAoYygiQnJhaW4iLCJQYW5jcmVhcyIsIkxpdmVyIiksIGMoMywzLDMpKSkJCmBgYAoKRm9yIGVkZ2VSIHRoZSAiREUgZ2VuZSIgb2JqZWN0IGNvbnRhaW5pbmcgYWxsIHRoZSBpbmZvIGFib3V0IHRoZSBkYXRhc2V0IGFzIHdlbGwgYXMgdGhlIHBhcmFtZXRlcnMgZXN0aW1hdGVkIGR1cmluZyB0aGUgZGlmZmVyZW50IHN0ZXBzIG9mIHRoZSBhbmFseXNpcyBpcyBhICJER0VMaXN0Ijsgd2UgYWRkIHRoZSBjb3VudHMgdG8gaXQuIApgYGB7cn0KI2NvdW50VGFibGVbaXMubmEoY291bnRUYWJsZSldID0gMAp5IDwtIERHRUxpc3QoY291bnRzPWNvdW50VGFibGUpCnkkc2FtcGxlcyRncm91cCA8LSBncm91cAp5CmBgYAoKYGBge3J9CmVuc2VtYmwgPC0gZ3N1YigiLipSIiwiIixyb3duYW1lcyh5JHNhbXBsZXMpKQpiYXJwbG90KHkkc2FtcGxlcyRsaWIuc2l6ZSoxZS02LCBuYW1lcz1lbnNlbWJsLCB5bGFiPSJMaWJyYXJ5IHNpemUgKG1pbGxpb25zKSIsIGxhcz0yLCBjb2w9cmVwKDI6NCwgZWFjaD0zKSkKbGVnZW5kKCJ0b3AiLCBsZWdlbmQgPSBjKCJCcmFpbiIsICJQYW5jcmVhcyIsICJMaXZlciIpLCBmaWxsPXJlcCgyOjQsIGVhY2g9MSksIGJ0eSA9ICJuIiwgaW5zZXQgPSBjKC0wLjA1LC0wLjEpLCB4cGQ9VFJVRSwgaG9yaXo9VCkKYGBgCgpOb3RpY2UgdGhhdCAibm9ybS5mYWN0b3JzIiBpcyBzdGlsbCBzZXQgdG8gb25lLiBGaXJzdCwgaXQgaXMgYWR2aXNhYmxlIHRvIHJlbW92ZSBhbHRvZ2V0aGVyIGFsbCBnZW5lcyB3aXRoIGxvdyBvciB6ZXJvIGNvdW50cy4KYGBge3J9CnRhYmxlKHJvd1N1bXMoeSRjb3VudHM9PTApPT05KQprZWVwLmV4cHJzIDwtIGZpbHRlckJ5RXhwcih5LCBncm91cD1ncm91cCkKeSA8LSB5W2tlZXAuZXhwcnMsLCBrZWVwLmxpYi5zaXplcz1GQUxTRV0KYGBgCgpGdW5jdGlvbiBrZWVwLmV4cHJzIHBlcm1pdHMgYWxzbyB0byBjaGFuZ2UgdGhlIHBhcmFtZXRlcnMgb2YgZmlsdGVyaW5nLiBMZXQgdXMgZXh0cmFjdCBhbmQgc3RvcmUgaW4gYSB2ZWN0b3IgdGhlIGxvZyBvZiB0aGUgY291bnRzIHBlciBtaWxsaW9uIGJlZm9yZSBub3JtYWxpemF0aW9uIHdpdGggdGhlICJjcG0iIGZ1bmN0aW9uLgpgYGB7cn0KbG9nY3BtX2JlZm9yZSA8LSBjcG0oeSwgbG9nPVRSVUUpCmBgYAoKV2UgY2FuIG5vdyBub3JtYWxpemUgdGhlIGNvdW50cy4gVE1NICh0cmltbWVkIG1lYW4gb2YgTS12YWx1ZXMpIGlzIHJlY29tbWVuZGVkIGZvciBtb3N0IFJOQS1TZXEgZGF0YSB3aGVyZSB0aGUgbWFqb3JpdHkgKG1vcmUgdGhhbiBoYWxmKSBvZiB0aGUgZ2VuZXMgYXJlIGJlbGlldmVkIG5vdCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYmV0d2VlbiBhbnkgcGFpciBvZiB0aGUgc2FtcGxlcy4KYGBge3J9CnkgPC0gY2FsY05vcm1GYWN0b3JzKHksIG1ldGhvZCA9ICJUTU0iKQp5CmBgYAoKTm90aWNlIG5vdyAibm9ybS5mYWN0b3JzIiBoYXMgY2hhbmdlZCwgYW5kIGRlc3BpdGUgcXVpdGUgcmVsZXZhbnQgZGlmZmVyZW5jZXMgaW4gbGlicmFyeSBzaXplcywgdGhleSByZW1haW4gcXVpdGUgbW9kZXJhdGUuIERpc3RyaWJ1dGlvbiBvZiAobm9ybWFsaXplZCkgbG9nLWNwbSB2YWx1ZXMgYWNyb3NzIHNhbXBsZXM6CmBgYHtyfQpsb2djcG0gPC0gY3BtKHksIGxvZz1UUlVFKQpib3hwbG90KGxvZ2NwbSwgbGFzPTIsIG5hbWVzID0gZW5zZW1ibCwgY29sPXJlcCgyOjQsIGVhY2g9MyksIG5vdGNoID0gVFJVRSkKbGVnZW5kKCJ0b3AiLCBsZWdlbmQgPSBjKCJCcmFpbiIsICJQYW5jcmVhcyIsICJMaXZlciIpLCBmaWxsPXJlcCgyOjQsIGVhY2g9MSksIGJ0eSA9ICJuIiwgaW5zZXQgPSBjKC0wLjA1LC0wLjEpLCB4cGQ9VFJVRSwgaG9yaXo9VCkKYGBgCgpCb3hwbG90cyBvZiB0aGUgbG9nMihub3JtYWxpemVkX2NvdW50cykgb2YgZWFjaCBzYW1wbGUsIG9uZSBib3hwbG90IHBlciBzYW1wbGUgd2l0aG91dCBvdXRsaWVycy4gCmBgYHtyfQpib3hwbG90KGxvZ2NwbSwgbGFzPTIsIG5hbWVzID0gZW5zZW1ibCwgY29sPXJlcCgyOjQsIGVhY2g9MyksIG91dGxpbmU9RkFMU0UgLCBub3RjaCA9IFRSVUUpCmxlZ2VuZCgidG9wIiwgbGVnZW5kID0gYygiQnJhaW4iLCAiUGFuY3JlYXMiLCAiTGl2ZXIiKSwgZmlsbD1yZXAoMjo0LCBlYWNoPTEpLCBidHkgPSAibiIsIGluc2V0ID0gYygtMC4wNSwtMC4xKSwgeHBkPVRSVUUsIGhvcml6PVQpCmBgYAoKQW5kIGJlZm9yZSBub3JtYWxpemF0aW9uOgpgYGB7cn0KYm94cGxvdChsb2djcG1fYmVmb3JlLCBsYXM9MiwgbmFtZXMgPSBlbnNlbWJsLCBjb2w9cmVwKDI6NCwgZWFjaD0zKSwgbm90Y2ggPSBUUlVFKQpsZWdlbmQoInRvcCIsIGxlZ2VuZCA9IGMoIkJyYWluIiwgIlBhbmNyZWFzIiwgIkxpdmVyIiksIGZpbGw9cmVwKDI6NCwgZWFjaD0xKSwgYnR5ID0gIm4iLCBpbnNldCA9IGMoLTAuMDUsLTAuMSksIHhwZD1UUlVFLCBob3Jpej1UKQpgYGAKClRoZSBjaGFuZ2UgaXMgc21hbGwsIGJ1dCBpdCBjYW4gYmUgbm90aWNlZCBieSBleWUuIE5vdyB3ZSBkZXNpZ24gdGhlIGxpbmVhciBtb2RlbDsgd2UgZG9u4oCZdCBoYXZlIHRvIGRlZmluZSBhbiAiaW50ZXJjZXB0IiB0ZXJtIGZvciBvdXIgbW9kZWwuIFRoZSBpbnRlcmNlcHQgaXMgb24gdGhlIG9yaWdpbiBzaW5jZSB0aGVyZSBpcyBubyAiYmFzZSBjb25kaXRpb24iIChlLmcuIGNhbmNlciBjZWxscyB2cyAibm9ybWFsIiBjZWxscykgZnJvbSB3aGljaCB0aGUgb3RoZXJzIGNhbiBiZSByZWxhdGVkIGJ5IGEgY2hhbmdlLgpgYGB7cn0KZGVzaWduIDwtIG1vZGVsLm1hdHJpeCh+MCtncm91cCwgZGF0YT15JHNhbXBsZXMpCmNvbG5hbWVzKGRlc2lnbikgPC0gbGV2ZWxzKHkkc2FtcGxlcyRncm91cCkKZGVzaWduCmBgYAoKQXQgdGhpcyBwb2ludCwgY291bnRzIGhhdmUgYmVlbiBub3JtYWxpemVkIGFuZCB0aGUgZGVzaWduIGRlZmluZWQuCgpFeHBsb3JhdG9yeSBhbmFseXNpczogd2UgcGxvdCB0aGUgc2FtcGxlcyBsYWJlbGVkIGJ5IGdyb3VwIGFuZCB0aGVuIGJ5IHJlcGxpY2F0ZS4KYGBge3J9CnBsb3RNRFMobG9nY3BtLCBsYWJlbHM9Z3JvdXAsIGNvbD1yZXAoMjo0LCBlYWNoPTMpKQpwbG90TURTKHksIGNvbD1yZXAoMjo0LCBlYWNoPTMpKQkKYGBgCgpNRFMgaXMgYSBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24sIGluIHdoaWNoIGluc3RlYWQgb2YgZXhwcmVzc2lvbiB0aGUgZm9sZCByYXRpbyB2YWx1ZXMgYW1vbmcgdGhlIHRocmVlIHNhbXBsZXMgYXJlIGVtcGxveWVkLiBXZSBub3cgZXN0aW1hdGUgdGhlIE5CIGRpc3BlcnNpb24sIGFuZCBwbG90IHRoZSBCQ1YuCmBgYHtyfQpsaWJyYXJ5KHN0YXRtb2QpCnkgPC0gZXN0aW1hdGVEaXNwKHksIGRlc2lnbiwgcm9idXN0PVRSVUUpCnBsb3RCQ1YoeSwgbWFpbj0iQkNWIFBsb3QiKQpgYGAKCkFsbCB0aGUgcGFyYW1ldGVycyBoYXZlIGJlZW4gc3RvcmVkIGluIHRoZSB5IG9iamVjdCwgYW1vbmcgd2hpY2ggdGhlICJjb21tb24iIGFuZCBnZW5lLXNwZWNpZmljIGRpc3BlcnNpb24gZXN0aW1hdGVzLgpgYGB7cn0KeSRjb21tb24uZGlzcGVyc2lvbgpgYGAKCk5vdyB3ZSBmaXQgdGhlIGRhdGEgdG8gdGhlICJnZW5lcmFsaXplZCBsaW5lYXIiIG1vZGVsLgpgYGB7cn0KZml0IDwtIGdsbVFMRml0KHksIGRlc2lnbiwgcm9idXN0PVRSVUUpCnBsb3RRTERpc3AoZml0KQpgYGAKCkZvciB0ZXN0aW5nIGZvciBERSBnZW5lcywgd2UgaGF2ZSB0byBzcGVjaWZ5IHRoZSBjb250cmFzdC4gVGhlIGZvbGxvd2luZyBpcyBjb25kaXRpb24gMSAoYnJhaW4pIHZzIGNvbmRpdGlvbiAyIChwYW5jcmVhcykuIHRvcFRhZ3MgcmV0dXJucyB0aGUgZ2VuZXMgd2l0aCB0aGUgaGlnaGVzdCB2YXJpYXRpb24uCmBgYHtyfQpxbGYuMXZzMiA8LSBnbG1RTEZUZXN0KGZpdCwgY29udHJhc3Q9YygxLDAsLTEpKSAjIGtlZXAgaW4gbWluZCBncm91cGluZyAoc2VlIGRlc2lnbiBtYXRyaXgpIGZvbGxvd3MgYWxwaGFiZXRpY2FsIG9yZGVyLCB0aGVyZWZvcmUgMSAtPiBCcmFpbiAwIC0+IExpdmVyIC0xIC0+IFBhbmNyZWFzCnRvcFRhZ3MocWxmLjF2czIpCmBgYAoKUGxvdCBsb2ctZm9sZCBjaGFuZ2UgYWdhaW5zdCBsb2ctY291bnRzIHBlciBtaWxsaW9uLCB3aXRoIERFIGdlbmVzIGhpZ2hsaWdodGVkOgpgYGB7cn0KcGxvdE1EKHFsZi4xdnMyKQphYmxpbmUoaD1jKDEsIDAsIC0xKSwgY29sPSJibHVlIikKYGBgCgpUaGUgY29tcGxldGUgcmVzdWx0cyBvZiB0aGUgdGVzdCBhcmUgaW4gcWxmLjF2czIkdGFibGUuIExldCB1cyBzZWxlY3QgdGhlIG9uZXMgd2l0aCBwLXZhbHVlIChGRFIpIDwgMC4wNS4KYGBge3J9CkZEUiA8LSBwLmFkanVzdChxbGYuMXZzMiR0YWJsZSRQVmFsdWUsIG1ldGhvZD0iQkgiKQpzdW0oRkRSIDwgMC4wNSkKYGBgCgpPcjoKYGBge3J9CnN1bW1hcnkoZGVjaWRlVGVzdHMocWxmLjF2czIpKQpgYGAKCmRlY2lkZVRlc3RzIGhhcyBhIGRlZmF1bHQgRkRSIChCSCBhZGp1c3RlZCBwLXZhbHVlKSB0aHJlc2hvbGQgb2YgMC4wNSwgYW5kIG5vIGNoZWNrIG9uIHRoZSBsb2ctZm9sZCByYXRpby4gV2UgY2FuIG1ha2UgdGhlIHNlbGVjdGlvbiBtb3JlIHN0cmluZ2VudCwgYnkgc2V0dGluZyBzdHJpY3RlciB0aHJlc2hvbGRzIGZvciBib3RoIGFuZCBzZXR0aW5nIGxmYyAobG9nLWZvbGQtY2hhbmdlKSA9IDEvLTEuCmBgYHtyfQpzdW1tYXJ5KGRlY2lkZVRlc3RzKHFsZi4xdnMyLCBwLnZhbHVlPTAuMDEsIGxmYz0xKSkKYGBgCldlIHJldGFpbiBwLXZhbHVlID0gMC4wMSBpcyBhIGJldHRlciBhbmQgbW9yZSBzaWduaWZpY2FudCB2YWx1ZSBzaW5jZSBtb3N0IG9mIHRoZSB0b29scyBoYXZlIGEgdGhyZXNob2xkIGFyb3VuZCAyLDAwMCBmb3IgdGhlIG1heGltdW0gbnVtYmVyIG9mIGdlbmVzIHRoYXQgY2FuIGJlIHN1Ym1pdHRlZCBmb3IgYW55IGFuYWx5c2lzIG9mIHRoaXMga2luZC4gCgpIZXJlIHdlIHNhdmUgdGhlIGxpc3Qgb2YgZ2VuZXMgd2l0aCBhZGp1c3RlZCBwLXZhbHVlIChGRFIpIGxvd2VyIHRoYW4gMC4wMS4KYGBge3J9CmRlZy4xdnMyIDwtIHRvcFRhZ3MocWxmLjF2czIsIG49SW5mLCBhZGp1c3QubWV0aG9kID0gIkJIIiwgc29ydC5ieSA9ICJQVmFsdWUiLCBwLnZhbHVlID0gMC4wMSkkdGFibGUKaGVhZChkZWcuMXZzMikKYGBgCgpXZSBjYW4gcGVyZm9ybSB0aGUgY29tcGFyaXNpb24gb2YgY29uZGl0aW9uIDEgdnMuIDMgaW4gYSBzaW1pbGFyIHdheS4KYGBge3J9CnFsZi4xdnMzIDwtIGdsbVFMRlRlc3QoZml0LCBjb250cmFzdD1jKDEsLTEsMCkpCmRlZy4xdnMzIDwtIHRvcFRhZ3MocWxmLjF2czMsIG49SW5mLCBhZGp1c3QubWV0aG9kID0gIkJIIiwgc29ydC5ieSA9ICJQVmFsdWUiLCBwLnZhbHVlID0gMC4wMSkkdGFibGUKaGVhZChkZWcuMXZzMykKYGBgCgpCdXQgYWxzbyBjb250cmFzdCBjb25kaXRpb24gMiB2cy4gMy4KYGBge3J9CnFsZi4ydnMzIDwtIGdsbVFMRlRlc3QoZml0LCBjb250cmFzdD1jKDAsLTEsMSkpCmRlZy4ydnMzIDwtIHRvcFRhZ3MocWxmLjJ2czMsIG49SW5mLCBhZGp1c3QubWV0aG9kID0gIkJIIiwgc29ydC5ieSA9ICJQVmFsdWUiLCBwLnZhbHVlID0gMC4wMSkkdGFibGUKaGVhZChkZWcuMnZzMykKYGBgCgpOb3cgd2UgaGF2ZSBhbGwgb3VyIHJlc3VsdHMsIGNvbnRhaW5lZCBoZXJlLgpgYGB7cn0KaGVhZChxbGYuMXZzMiR0YWJsZSkKaGVhZChxbGYuMXZzMyR0YWJsZSkKaGVhZChxbGYuMnZzMyR0YWJsZSkKYGBgCgpOb3cgd2Ugd2FudCB0byByZXBsYWNlIHRoZSBFTlNHIElEcyB3aXRoIHRoZSBnZW5lIG5hbWUvc3ltYm9sLCBidXQgaW5zdGVhZCBvZiByZXBsYWNpbmcgdGhlIG5hbWVzIGluIHRoZSB0YWJsZSBpdCBpcyBiZXR0ZXIgdG8gYWRkIGFuIGFkZGl0aW9uYWwgY29sdW1uIHRvIGl0LiBUaGUgb3JnLkhzLmVnLmRiIGlzIGEgUiBwYWNrYWdlIGNvbnRhaW5pbmcgdGhlIGdlbmUgYW5ub3RhdGlvbiBmb3IgaHVtYW4gKHNhbWUgZm9yIG1vdXNlIGlmIHlvdSByZXBsYWNlIEhzIHdpdGggTW0sIGFuZCBzbyBvbikgaW4gd2hpY2ggZm9yIGVhY2ggZ2VuZSBpbiBlYWNoIGFubm90YXRpb24gdGhlcmUgaXMgaXRzIGNvcnJlc3BvbmRlbmNlIHRvIGFub3RoZXIgYW5ub3RhdGlvbi4gV2UgY2FuIGVtcGxveSBpdCB0byAidHJhbnNsYXRlIiBnZW5lIElEcyBvciBuYW1lcyBmcm9tIG9uZSBhbm5vdGF0aW9uIHRvIHRoZSBvdGhlci4gV2UgYWxzbyBhZGQgYW4gImVudHJlemlkIiBjb2x1bW4sIHVzZWZ1bCBmb3Igc3Vic2VxdWVudCBhbmFseXNpcyBzaW5jZSBHTyBhbmQgS0VHRyAodGhlIHRvb2xzIHdlJ2xsIHVzZSkgd29yayBvbiB0aGF0IG9uZS4KYGBge3J9CmxpYnJhcnkoIm9yZy5Icy5lZy5kYiIpCnFsZi4xdnMyJHRhYmxlJHN5bWJvbCA8LSBtYXBJZHMob3JnLkhzLmVnLmRiLGtleXM9cm93bmFtZXMocWxmLjF2czIkdGFibGUpLCBrZXl0eXBlPSJFTlNFTUJMIiwgY29sdW1uPSJTWU1CT0wiLCBtdWx0aVZhbHM9ImZpcnN0IikJCQkKcWxmLjF2czIkdGFibGUkZW50cmV6aWQgPC0gbWFwSWRzKG9yZy5Icy5lZy5kYiwga2V5cz1yb3duYW1lcyhxbGYuMXZzMiR0YWJsZSksIGNvbHVtbj0iRU5UUkVaSUQiLCBrZXl0eXBlPSJFTlNFTUJMIiwgbXVsdGlWYWxzPSJmaXJzdCIpCnFsZi4xdnMzJHRhYmxlJHN5bWJvbCA8LSBtYXBJZHMob3JnLkhzLmVnLmRiLGtleXM9cm93bmFtZXMocWxmLjF2czMkdGFibGUpLCBrZXl0eXBlPSJFTlNFTUJMIiwgY29sdW1uPSJTWU1CT0wiLCBtdWx0aVZhbHM9ImZpcnN0IikKcWxmLjF2czMkdGFibGUkZW50cmV6aWQgPC0gbWFwSWRzKG9yZy5Icy5lZy5kYiwga2V5cz1yb3duYW1lcyhxbGYuMXZzMyR0YWJsZSksIGNvbHVtbj0iRU5UUkVaSUQiLCBrZXl0eXBlPSJFTlNFTUJMIiwgbXVsdGlWYWxzPSJmaXJzdCIpCnFsZi4ydnMzJHRhYmxlJHN5bWJvbCA8LSBtYXBJZHMob3JnLkhzLmVnLmRiLGtleXM9cm93bmFtZXMocWxmLjJ2czMkdGFibGUpLCBrZXl0eXBlPSJFTlNFTUJMIiwgY29sdW1uPSJTWU1CT0wiLCBtdWx0aVZhbHM9ImZpcnN0IikKcWxmLjJ2czMkdGFibGUkZW50cmV6aWQgPC0gbWFwSWRzKG9yZy5Icy5lZy5kYiwga2V5cz1yb3duYW1lcyhxbGYuMnZzMyR0YWJsZSksIGNvbHVtbj0iRU5UUkVaSUQiLCBrZXl0eXBlPSJFTlNFTUJMIiwgbXVsdGlWYWxzPSJmaXJzdCIpCmhlYWQocWxmLjF2czIkdGFibGUpCmhlYWQocWxmLjF2czMkdGFibGUpCmhlYWQocWxmLjJ2czMkdGFibGUpCmBgYAoKTm93IHdlIGNhbiBzZWxlY3Qgd2hhdCB3ZSBjb25zaWRlciB0byBiZSB0aGUgIkRFIiBnZW5lcyBmcm9tIHRoZSB0YWJsZS4gV2Ugc2VsZWN0IHRoZW0gYnkgY29ycmVjdGVkIHAtdmFsdWUgZmlyc3QsIHRoZW4gYnkgRkMgKHBvc2l0aXZlL25lZ2F0aXZlKS4KYGBge3J9CmRlZy4xdnMyIDwtIHRvcFRhZ3MocWxmLjF2czIsIG49SW5mLCBhZGp1c3QubWV0aG9kID0gIkJIIiwgc29ydC5ieSA9ICJQVmFsdWUiLCBwLnZhbHVlID0gMC4wMSkkdGFibGUKZGVnLjF2czMgPC0gdG9wVGFncyhxbGYuMXZzMywgbj1JbmYsIGFkanVzdC5tZXRob2QgPSAiQkgiLCBzb3J0LmJ5ID0gIlBWYWx1ZSIsIHAudmFsdWUgPSAwLjAxKSR0YWJsZQpkZWcuMnZzMyA8LSB0b3BUYWdzKHFsZi4ydnMzLCBuPUluZiwgYWRqdXN0Lm1ldGhvZCA9ICJCSCIsIHNvcnQuYnkgPSAiUFZhbHVlIiwgcC52YWx1ZSA9IDAuMDEpJHRhYmxlCmhlYWQoZGVnLjF2czIpCmhlYWQoZGVnLjF2czMpCmhlYWQoZGVnLjJ2czMpCmBgYApBbmQgdGhlbiByZXRyaWV2ZSB0aGUgbGlzdHMgb2YgInVwIiBhbmQgImRvd24iIHJlZ3VsYXRlZCBvbmVzLgpgYGB7cn0KdXAuZ2VuZXMuMXZzMiA8LSBkZWcuMXZzMltkZWcuMXZzMiRsb2dGQyA+IDAsXQp1cC5nZW5lcy4xdnMzIDwtIGRlZy4xdnMzW2RlZy4xdnMzJGxvZ0ZDID4gMCxdCnVwLmdlbmVzLjJ2czMgPC0gZGVnLjJ2czNbZGVnLjJ2czMkbG9nRkMgPiAwLF0KaGVhZCh1cC5nZW5lcy4xdnMyKQpoZWFkKHVwLmdlbmVzLjF2czMpCmhlYWQodXAuZ2VuZXMuMnZzMykKYGBgCmBgYHtyfQpkb3duLmdlbmVzLjF2czIgPC0gZGVnLjF2czJbZGVnLjF2czIkbG9nRkMgPCAwLF0KZG93bi5nZW5lcy4xdnMzIDwtIGRlZy4xdnMzW2RlZy4xdnMzJGxvZ0ZDIDwgMCxdCmRvd24uZ2VuZXMuMnZzMyA8LSBkZWcuMnZzM1tkZWcuMnZzMyRsb2dGQyA8IDAsXQpoZWFkKGRvd24uZ2VuZXMuMXZzMikKaGVhZChkb3duLmdlbmVzLjF2czMpCmhlYWQoZG93bi5nZW5lcy4ydnMzKQpgYGAKCkZpbmFsbHksIHdlIGNhbiBzYXZlIEV4Y2VsIHNoZWV0cyBmb3IgZnVydGhlciB1c2UuCmBgYHtyfQpsaWJyYXJ5KCJ4bHN4IikKd3JpdGUueGxzeChkZWcuMXZzMiwgZmlsZT0iZGVnLjF2czIueGxzeCIsIHNoZWV0TmFtZSA9ICJhIiwgY29sLm5hbWVzID0gVFJVRSwgcm93Lm5hbWVzID0gVFJVRSwgYXBwZW5kID0gRkFMU0UpCndyaXRlLnhsc3goZGVnLjF2czMsIGZpbGU9ImRlZy4xdnMzLnhsc3giLCBzaGVldE5hbWUgPSAiYiIsIGNvbC5uYW1lcyA9IFRSVUUsIHJvdy5uYW1lcyA9IFRSVUUsIGFwcGVuZCA9IEZBTFNFKQp3cml0ZS54bHN4KGRlZy4ydnMzLCBmaWxlPSJkZWcuMnZzMy54bHN4Iiwgc2hlZXROYW1lID0gImMiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBhcHBlbmQgPSBGQUxTRSkKCiN1cCAxdnMyMwpsIDwtIGxpc3QodXAuZ2VuZXMuMXZzMiwgdXAuZ2VuZXMuMXZzMykKY29tbW9uX25hbWVzID0gUmVkdWNlKGludGVyc2VjdCwgbGFwcGx5KGwsIHJvdy5uYW1lcykpCmwgPC0gbGFwcGx5KGwsIGZ1bmN0aW9uKHgpIHsgeFtyb3cubmFtZXMoeCkgJWluJSBjb21tb25fbmFtZXMsXSB9KQpsaWJyYXJ5KGRwbHlyKQpsIDwtIGxhcHBseShsLCBmdW5jdGlvbih4KSB4JT4lIHNlbGVjdChzeW1ib2wsZW50cmV6aWQpKQojIGwgaXMgYSBsaXN0IGNvbnRhaW5pbmcgdHdvIGRhdGFmcmFtZXMgd2l0aCBzYW1lIHJvd25hbWVzLCBzeW1ib2xzICYgZW50cmV6aWRzLCBhcyBleHBlY3RlZC4gWW91IGNhbiBjaGVjayBpdCB1c2luZyBsMSA8LSBsW1sxXV07IGwxIDwtIGwxWyBvcmRlcihyb3cubmFtZXMobDEpKSwgXTsgbDIgPC0gbFtbMl1dOyBsMiA8LSBsMlsgb3JkZXIocm93Lm5hbWVzKGwyKSksIF07IGwxID09IGwyCiMgVGhlcmVmb3JlIHdlIGNhbiBwaWNrIGVpdGhlciBsW1sxXV0gb3IgbFtbMl1dCndyaXRlLnhsc3gobFtbMV1dLCBmaWxlPSJ1cC4xdnMyMy54bHN4Iiwgc2hlZXROYW1lID0gImQtMXZzMjMiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBhcHBlbmQgPSBGQUxTRSkgCgojdXAgMnZzMTMKbCA8LSBsaXN0KHVwLmdlbmVzLjF2czIsIHVwLmdlbmVzLjJ2czMpCmNvbW1vbl9uYW1lcyA9IFJlZHVjZShpbnRlcnNlY3QsIGxhcHBseShsLCByb3cubmFtZXMpKQpsIDwtIGxhcHBseShsLCBmdW5jdGlvbih4KSB7IHhbcm93Lm5hbWVzKHgpICVpbiUgY29tbW9uX25hbWVzLF0gfSkKbCA8LSBsYXBwbHkobCwgZnVuY3Rpb24oeCkgeCU+JSBzZWxlY3Qoc3ltYm9sLGVudHJlemlkKSkKd3JpdGUueGxzeChsW1sxXV0sIGZpbGU9InVwLjJ2czEzLnhsc3giLCBzaGVldE5hbWUgPSAiZC0ydnMxMyIsIGNvbC5uYW1lcyA9IFRSVUUsIHJvdy5uYW1lcyA9IFRSVUUsIGFwcGVuZCA9IEZBTFNFKSAKCiN1cCAzdnMxMgpsIDwtIGxpc3QodXAuZ2VuZXMuMXZzMywgdXAuZ2VuZXMuMnZzMykKY29tbW9uX25hbWVzID0gUmVkdWNlKGludGVyc2VjdCwgbGFwcGx5KGwsIHJvdy5uYW1lcykpCmwgPC0gbGFwcGx5KGwsIGZ1bmN0aW9uKHgpIHsgeFtyb3cubmFtZXMoeCkgJWluJSBjb21tb25fbmFtZXMsXSB9KQpsIDwtIGxhcHBseShsLCBmdW5jdGlvbih4KSB4JT4lIHNlbGVjdChzeW1ib2wsZW50cmV6aWQpKQp3cml0ZS54bHN4KGxbWzFdXSwgZmlsZT0idXAuM3ZzMTIueGxzeCIsIHNoZWV0TmFtZSA9ICJkLTN2czEyIiwgY29sLm5hbWVzID0gVFJVRSwgcm93Lm5hbWVzID0gVFJVRSwgYXBwZW5kID0gRkFMU0UpCgojZG93biAxdnMyMwpsIDwtIGxpc3QoZG93bi5nZW5lcy4xdnMyLCBkb3duLmdlbmVzLjF2czMpCmNvbW1vbl9uYW1lcyA9IFJlZHVjZShpbnRlcnNlY3QsIGxhcHBseShsLCByb3cubmFtZXMpKQpsIDwtIGxhcHBseShsLCBmdW5jdGlvbih4KSB7IHhbcm93Lm5hbWVzKHgpICVpbiUgY29tbW9uX25hbWVzLF0gfSkKbCA8LSBsYXBwbHkobCwgZnVuY3Rpb24oeCkgeCU+JSBzZWxlY3Qoc3ltYm9sLGVudHJlemlkKSkKd3JpdGUueGxzeChsW1sxXV0sIGZpbGU9ImRvd24uMXZzMjMueGxzeCIsIHNoZWV0TmFtZSA9ICJlLTF2czIzIiwgY29sLm5hbWVzID0gVFJVRSwgcm93Lm5hbWVzID0gVFJVRSwgYXBwZW5kID0gRkFMU0UpIAoKI2Rvd24gMnZzMTMKbCA8LSBsaXN0KGRvd24uZ2VuZXMuMXZzMiwgZG93bi5nZW5lcy4ydnMzKQpjb21tb25fbmFtZXMgPSBSZWR1Y2UoaW50ZXJzZWN0LCBsYXBwbHkobCwgcm93Lm5hbWVzKSkKbCA8LSBsYXBwbHkobCwgZnVuY3Rpb24oeCkgeyB4W3Jvdy5uYW1lcyh4KSAlaW4lIGNvbW1vbl9uYW1lcyxdIH0pCmwgPC0gbGFwcGx5KGwsIGZ1bmN0aW9uKHgpIHglPiUgc2VsZWN0KHN5bWJvbCxlbnRyZXppZCkpCndyaXRlLnhsc3gobFtbMV1dLCBmaWxlPSJkb3duLjJ2czEzLnhsc3giLCBzaGVldE5hbWUgPSAiZS0ydnMxMyIsIGNvbC5uYW1lcyA9IFRSVUUsIHJvdy5uYW1lcyA9IFRSVUUsIGFwcGVuZCA9IEZBTFNFKSAKCiNkb3duIDN2czEyCmwgPC0gbGlzdChkb3duLmdlbmVzLjF2czMsIGRvd24uZ2VuZXMuMnZzMykKY29tbW9uX25hbWVzID0gUmVkdWNlKGludGVyc2VjdCwgbGFwcGx5KGwsIHJvdy5uYW1lcykpCmwgPC0gbGFwcGx5KGwsIGZ1bmN0aW9uKHgpIHsgeFtyb3cubmFtZXMoeCkgJWluJSBjb21tb25fbmFtZXMsXSB9KQpsIDwtIGxhcHBseShsLCBmdW5jdGlvbih4KSB4JT4lIHNlbGVjdChzeW1ib2wsZW50cmV6aWQpKQp3cml0ZS54bHN4KGxbWzFdXSwgZmlsZT0iZG93bi4zdnMxMi54bHN4Iiwgc2hlZXROYW1lID0gImUtM3ZzMTIiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBhcHBlbmQgPSBGQUxTRSkKYGBgCgpHZW5lIHVwLXJlZ3VsYXRlZCBpbiBUaXNzdWUgMSB2cyBUaXNzdWUgMiB3aXRoIHRoZSBsb3dlc3QgRkRSIGFuZCB1cC1yZWd1bGF0ZWQgYWxzbyBpbiBUaXNzdWUgMSB2cyBUaXNzdWUgMy4KYGBge3J9CmwgPC0gbGlzdChoZWFkKHVwLmdlbmVzLjF2czIpLCB1cC5nZW5lcy4xdnMzKQpjb21tb25fbmFtZXMgPSBSZWR1Y2UoaW50ZXJzZWN0LCBsYXBwbHkobCwgcm93Lm5hbWVzKSkKbCA8LSBsYXBwbHkobCwgZnVuY3Rpb24oeCkgeyB4W3Jvdy5uYW1lcyh4KSAlaW4lIGNvbW1vbl9uYW1lcyxdIH0pCmwgPC0gbGFwcGx5KGwsIGZ1bmN0aW9uKHgpIHglPiUgc2VsZWN0KHN5bWJvbCxlbnRyZXppZCkpCmxbWzFdXVsxLF0KYGBgCgojIEdPICYgS0VHRyBwYXRod2F5IGFuYWx5c2lzIAoKVGhlIGdlbmUgb250b2xvZ3kgKEdPKSBhbmQgdGhlIEtFR0cgcGF0aHdheSBhbmFseXNpcyBhcmUgdGhlIGNvbW1vbiBkb3duc3RyZWFtIHByb2NlZHVyZXMgdG8gaW50ZXJwcmV0IHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZXN1bHRzIGluIGEgYmlvbG9naWNhbCBjb250ZXh0LiBHaXZlbiBhIHNldCBvZiBnZW5lcyB0aGF0IGFyZSB1cC0gb3IgZG93bi1yZWd1bGF0ZWQgdW5kZXIgYSBjZXJ0YWluIGNvbnRyYXN0IG9mIGludGVyZXN0LCBhIEdPIChvciBwYXRod2F5KSBhbmFseXNpcyB3aWxsIGZpbmQgd2hpY2ggR08gdGVybXMgKG9yIHBhdGh3YXlzKSBhcmUgb3Zlci0gb3IgdW5kZXItcmVwcmVzZW50ZWQgdXNpbmcgYW5ub3RhdGlvbnMgZm9yIHRoZSBnZW5lcyBpbiB0aGF0IHNldC4gU3VwcG9zZSB3ZSB3YW50IHRvIGlkZW50aWZ5IEdPIHRlcm1zIGFuZCBLRUdHIHBhdGh3YXlzIGluIGdyb3VwIDEgKGJyYWluKSBjb21wYXJlZCB0byBncm91cCAyIChwYW5jcmVhcykgZnJvbSB0aGUgcHJldmlvdXMgYW5hbHlzaXMuCgkKRm9jdXNpbmcgb24gdGhlIG9udG9sb2d5IG9mIEJpb2xvZ2ljYWwgUHJvY2VzcyAoQlApIGFuZCBtZXRhYm9saWMgcGF0aHdheXMgKGdpdmVuIGJ5IEtFR0cpIHdlIGNhbiBvYnNlcnZlIHVwLXJlZ3VsYXRlZCBnZW5lcyB0aGF0IGluIHRoZSB0dW1vcnMgKG9yIG1vcmUgaW4gZ2VuZXJhbCBpbiBkaXNlYXNlcywgc2luY2Ugd2UgaGF2ZSBubyBjYW5jZXJvdXMgY2VsbHMgY29uZGl0aW9uKSB0ZW5kIHRvIGJlIGFzc29jaWF0ZWQgd2l0aCBjZWxsIGRpZmZlcmVudGlhdGlvbiwgY2VsbCBtaWdyYXRpb24gYW5kIHRpc3N1ZSBtb3JwaG9nZW5lc2lzLgpgYGB7cn0KZ28gPC0gZ29hbmEobGlzdChVcD11cC5nZW5lcy4xdnMyJGVudHJlemlkLCBEb3duPWRvd24uZ2VuZXMuMXZzMiRlbnRyZXppZCksIHNwZWNpZXM9IkhzIiwgRkRSPTAuMDEpCnRvcEdPKGdvLCBvbnRvbG9neSA9ICJCUCIsIG51bWJlciA9IDMwTCwgdHJ1bmNhdGUudGVybSA9IE5VTEwsIHNvcnQ9IlVwIikKa2VnIDwtIGtlZ2dhKGxpc3QoVXA9dXAuZ2VuZXMuMXZzMiRlbnRyZXppZCwgRG93bj1kb3duLmdlbmVzLjF2czIkZW50cmV6aWQpLCBzcGVjaWVzPSJIcyIsIEZEUj0wLjAxKQp0b3BLRUdHKGtlZywgbnVtYmVyID0gMzBMLCB0cnVuY2F0ZS5wYXRoID0gTlVMTCwgc29ydD0iVXAiKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKZWdvIDwtIGVucmljaEdPKHVwLmdlbmVzLjF2czIkZW50cmV6aWQsIE9yZ0RiPSJvcmcuSHMuZWcuZGIiLCBvbnQ9IkJQIiwgcmVhZGFibGU9VFJVRSwgcHZhbHVlQ3V0b2ZmPTAuMDEpCgpsaWJyYXJ5KGVucmljaHBsb3QpCmRvdHBsb3QoZWdvLCBzaG93Q2F0ZWdvcnk9MzApCgprayA8LSBlbnJpY2hLRUdHKHVwLmdlbmVzLjF2czIkZW50cmV6aWQsIG9yZ2FuaXNtID0gImhzYSIsIHB2YWx1ZUN1dG9mZj0wLjAxKQpkb3RwbG90KGtrLCBzaG93Q2F0ZWdvcnk9MzApCmBgYApUaGVzZSBidW5jaCBvZiBnZW5lcyB1cC1yZWd1bGF0ZWQgc2hvd2VkIGFib3ZlIGFyZSBhbGwgYXNzb2NpYXRlZCB3aXRoIHRoZSBuZXJ2b3VzIHN5c3RlbSAocHJvYmFibHkgdGhleSBhcmUgb3Zlci1leHByZXNzZWQgaW4gb2xkZXIgYWdlcyBhbmQgYWxzbyBpbiBjZXJlYnJvdmFzY3VsYXIgb3IgbmV1cm9sb2dpY2FsIGRpc2Vhc2VzLCBwcmluY2lwYWwgY2F1c2VzIG9mIGRlYXRoIG9mIHRoZXNlIGRvbm9ycykuCgpEb3duLXJlZ3VsYXRlZCAxdnMyIGdlbmVzIGFyZSBtb3N0bHkgYXNzb2NpYXRlZCB3aXRoIHBhbmNyZWF0aWMgZnVuY3Rpb25zLiBXZSBjYW4gYWxzbyBhcHByZWNpYXRlIHRoZSBwcmVzZW5jZSBvZiAiTWF0dXJpdHkgb25zZXQgZGlhYmV0ZXMgb2YgdGhlIHlvdW5nIiAoS0VHRykgd2hpY2ggcmVmZXJzIHRvIGFueSBvZiBzZXZlcmFsIGhlcmVkaXRhcnkgZm9ybXMgb2YgZGlhYmV0ZXMgbWVsbGl0dXMgY2F1c2VkIGJ5IG11dGF0aW9ucyBpbiBhbiBhdXRvc29tYWwgZG9taW5hbnQgZ2VuZSBkaXNydXB0aW5nIGluc3VsaW4gcHJvZHVjdGlvbiBpbiBpc2xldHMgb2YgTGFuZ2VyaGFucyBvZiBwYW5jcmVhcy4KYGBge3J9CnRvcEdPKGdvLCBvbnRvbG9neSA9ICJCUCIsIG51bWJlciA9IDMwTCwgdHJ1bmNhdGUudGVybSA9IE5VTEwsIHNvcnQ9IkRvd24iKQp0b3BLRUdHKGtlZywgbnVtYmVyID0gMzBMLCB0cnVuY2F0ZS5wYXRoID0gTlVMTCwgc29ydD0iRG93biIpCmBgYAoKYGBge3J9CmVnbyA8LSBlbnJpY2hHTyhkb3duLmdlbmVzLjF2czIkZW50cmV6aWQsIE9yZ0RiPSJvcmcuSHMuZWcuZGIiLCBvbnQ9IkJQIiwgcmVhZGFibGU9VFJVRSwgcHZhbHVlQ3V0b2ZmPTAuMDEpCmRvdHBsb3QoZWdvLCBzaG93Q2F0ZWdvcnk9MzApCmtrIDwtIGVucmljaEtFR0coZG93bi5nZW5lcy4xdnMyJGVudHJlemlkLCBvcmdhbmlzbSA9ICJoc2EiLCBwdmFsdWVDdXRvZmY9MC4wMSkKZG90cGxvdChraywgc2hvd0NhdGVnb3J5PTMwKQpgYGAKCldlIGNhbiBkbyB0aGUgc2FtZSBmb3IgZ3JvdXAgMSAoYnJhaW4pIGNvbXBhcmVkIHRvIGdyb3VwIDMgKGxpdmVyKS4KYGBge3J9CmdvIDwtIGdvYW5hKGxpc3QoVXA9dXAuZ2VuZXMuMXZzMyRlbnRyZXppZCwgRG93bj1kb3duLmdlbmVzLjF2czMkZW50cmV6aWQpLCBzcGVjaWVzPSJIcyIsIEZEUj0wLjAxKQp0b3BHTyhnbywgb250b2xvZ3kgPSAiQlAiLCBudW1iZXIgPSAzMEwsIHRydW5jYXRlLnRlcm0gPSBOVUxMLCBzb3J0ID0gIlVwIikKa2VnIDwtIGtlZ2dhKGxpc3QoVXA9dXAuZ2VuZXMuMXZzMyRlbnRyZXppZCwgRG93bj1kb3duLmdlbmVzLjF2czMkZW50cmV6aWQpLCBzcGVjaWVzPSJIcyIsIEZEUj0wLjAxKQp0b3BLRUdHKGtlZywgbnVtYmVyID0gMzBMLCB0cnVuY2F0ZS5wYXRoID0gTlVMTCwgc29ydD0iRG93biIpCmBgYAoKYGBge3J9CmVnbyA8LSBlbnJpY2hHTyh1cC5nZW5lcy4xdnMzJGVudHJlemlkLCBPcmdEYj0ib3JnLkhzLmVnLmRiIiwgb250PSJCUCIsIHJlYWRhYmxlPVRSVUUsIHB2YWx1ZUN1dG9mZj0wLjAxKQpkb3RwbG90KGVnbywgc2hvd0NhdGVnb3J5PTMwKQprayA8LSBlbnJpY2hLRUdHKHVwLmdlbmVzLjF2czMkZW50cmV6aWQsIG9yZ2FuaXNtID0gImhzYSIsIHB2YWx1ZUN1dG9mZj0wLjAxKQpkb3RwbG90KGtrLCBzaG93Q2F0ZWdvcnk9MzApCmBgYAoKV2UgY2FuIHNlZSBzaW1pbGFyIHJlc3VsdHMsIHVwLXJlZ3VsYXRlZCBnZW5lcyBhcmUgYXNzb2NpYXRlZCB3aXRoIG5ldXJhbCBmdW5jdGlvbnMuIExldCdzIGdvIGZvciBkb3duLXJlZ3VsYXRlZCBnZW5lcy4KYGBge3J9CnRvcEdPKGdvLCBvbnRvbG9neSA9ICJCUCIsIG51bWJlciA9IDMwTCwgdHJ1bmNhdGUudGVybSA9IE5VTEwsIHNvcnQ9IkRvd24iKQp0b3BLRUdHKGtlZywgbnVtYmVyID0gMzBMLCB0cnVuY2F0ZS5wYXRoID0gTlVMTCxzb3J0PSJEb3duIikKYGBgCgpgYGB7cn0KZWdvIDwtIGVucmljaEdPKGRvd24uZ2VuZXMuMXZzMyRlbnRyZXppZCwgT3JnRGI9Im9yZy5Icy5lZy5kYiIsIG9udD0iQlAiLCByZWFkYWJsZT1UUlVFLCBwdmFsdWVDdXRvZmY9MC4wMSkKZG90cGxvdChlZ28sIHNob3dDYXRlZ29yeT0xNSkKa2sgPC0gZW5yaWNoS0VHRyhkb3duLmdlbmVzLjF2czMkZW50cmV6aWQsIG9yZ2FuaXNtID0gImhzYSIsIHB2YWx1ZUN1dG9mZj0wLjAxKQpkb3RwbG90KGtrLCBzaG93Q2F0ZWdvcnk9MzApCmBgYApSZWFjaGluZyBzaW1pbGFyIHJlc3VsdHMgb2YgMXZzMi4gRG93bi1yZWd1bGF0ZWQgZ2VuZXMgYXJlIGFzc29jaWF0ZWQgd2l0aCBoZXBhdGljIChpbnN0ZWFkIG9mIHBhbmNyZWF0aWMpIGZ1bmN0aW9ucywgc3VjaCBhcyBtZXRhYm9saWMgb25lcywgYmlsZSBzZWNyZXRpb24sIGhvcm1vbmUgYmlvc3ludGhlc2lzIGFuZCBzbyBvbiBhbmQgc28gZm9ydGguCgpGaW5hbGx5LCBncm91cCAyIChwYW5jcmVhcykgY29tcGFyZWQgdG8gZ3JvdXAgMyAobGl2ZXIpLgpgYGB7cn0KZ28gPC0gZ29hbmEobGlzdChVcD11cC5nZW5lcy4ydnMzJGVudHJlemlkLCBEb3duPWRvd24uZ2VuZXMuMnZzMyRlbnRyZXppZCksIHNwZWNpZXM9IkhzIiwgRkRSPTAuMDEpCnRvcEdPKGdvLCBvbnRvbG9neSA9ICJCUCIsIG51bWJlciA9IDMwTCwgdHJ1bmNhdGUudGVybSA9IE5VTEwsIHNvcnQgPSAiVXAiKQprZWcgPC0ga2VnZ2EobGlzdChVcD11cC5nZW5lcy4ydnMzJGVudHJlemlkLCBEb3duPWRvd24uZ2VuZXMuMnZzMyRlbnRyZXppZCksIHNwZWNpZXM9IkhzIiwgRkRSPTAuMDEpCnRvcEtFR0coa2VnLCBudW1iZXIgPSAzMEwsIHRydW5jYXRlLnBhdGggPSBOVUxMLCBzb3J0ID0gIlVwIikKYGBgCgpgYGB7cn0KZWdvIDwtIGVucmljaEdPKHVwLmdlbmVzLjJ2czMkZW50cmV6aWQsIE9yZ0RiPSJvcmcuSHMuZWcuZGIiLCBvbnQ9IkJQIiwgcmVhZGFibGU9VFJVRSwgcHZhbHVlQ3V0b2ZmPTAuMDEpCmRvdHBsb3QoZWdvLCBzaG93Q2F0ZWdvcnk9MzApCmtrIDwtIGVucmljaEtFR0codXAuZ2VuZXMuMnZzMyRlbnRyZXppZCwgb3JnYW5pc20gPSAiaHNhIiwgcHZhbHVlQ3V0b2ZmPTAuMDEpCmRvdHBsb3Qoa2ssIHNob3dDYXRlZ29yeT0zMCkKYGBgCgpHb29nbGluZyBpbmZvcm1hdGlvbiBvbiB0aG9zZSBidW5jaCBvZiBnZW5lcyAoaHR0cHM6Ly93d3cuZWJpLmFjLnVrL1F1aWNrR08vKSB3ZSBzZWUgdGhhdCBtb3N0IG9mIHRoZXNlIHVwLXJlZ3VsYXRlZCBnZW5lcyAoYW5kIHRoZWlyIGZpbmFsIHByb2R1Y3RzKSBhcmUgYXNzb2NpYXRlZCB3aXRoIG1ldGFib2xpYyBwcm9jZXNzaW5nIHRha2luZyBwbGFjZSBpbiBib3RoIHBhbmNyZWF0aWMgYW5kIGhlcGF0aWMgY2VsbHMgKHdlIGNhbiBzZWUgYm90aCAiUGFuY3JlYXRpYyBzZWNyZXRpb24iIGFuZCAiR2FzdHJpYyBhY2lkIHNlY3JldGlvbiIgZm9yIGV4YW1wbGUpLiAKCkxldCdzIHNlZSBkb3duLXJlZ3VsYXRlZCBnZW5lcy4KYGBge3J9CnRvcEdPKGdvLCBvbnRvbG9neSA9ICJCUCIsIG51bWJlciA9IDMwTCwgdHJ1bmNhdGUudGVybSA9IE5VTEwsIHNvcnQ9IkRvd24iKQp0b3BLRUdHKGtlZywgbnVtYmVyID0gMzBMLCB0cnVuY2F0ZS5wYXRoID0gTlVMTCwgc29ydD0iRG93biIpCmBgYAoKYGBge3J9CmVnbyA8LSBlbnJpY2hHTyhkb3duLmdlbmVzLjJ2czMkZW50cmV6aWQsIE9yZ0RiPSJvcmcuSHMuZWcuZGIiLCBvbnQ9IkJQIiwgcmVhZGFibGU9VFJVRSwgcHZhbHVlQ3V0b2ZmPTAuMDEpCmRvdHBsb3QoZWdvLCBzaG93Q2F0ZWdvcnk9MzApCmtrIDwtIGVucmljaEtFR0coZG93bi5nZW5lcy4ydnMzJGVudHJlemlkLCBvcmdhbmlzbSA9ICJoc2EiLCBwdmFsdWVDdXRvZmY9MC4wMSkKZG90cGxvdChraywgc2hvd0NhdGVnb3J5PTMwKQpgYGAKCldlIG9idGFpbmVkIGEgc2ltaWxhciBsaXN0IG9mIGRvd24tcmVndWxhdGVkIGdlbmVzIG9mIDF2czM6IGRvd24tcmVndWxhdGVkIGdlbmVzIGFyZSBhc3NvY2lhdGVkIHdpdGggaGVwYXRpYyBmdW5jdGlvbnMuCgpUaGUgYWltIG9mIHRoZSBwb3N0LXByb2Nlc3NpbmcgYnVsayBSTkEtU2VxIGRhdGFzZXQgZ2l2ZW4gYnkgdGhpcyBsYXN0IGFuYWx5c2lzIGlzIGFjaGlldmVkLiBXZSB1c2VkIHR3byB0b29scyAoYW5kIGdlbmVyYXRlZCByZXNwZWN0aXZlIGNsdXN0ZXJQcm9maWxlciBkb3QgcGxvdHMpIGluIG9yZGVyIHRvIGRldGVybWluZSB3aGV0aGVyIHRoZSByZXN1bHRzICJtYWtlIHNlbnNlIjsgdGhlIEdPIGFubm90YXRpb25zICYgS0VHRyBwYXRod2F5cyBhcmUgY29uc2lzdGVudCB3aXRoIHRoZSBmYWN0IHRoYXQgdGhlIGdlbmVzIGFyZSB1cC1yZWd1bGF0ZWQgb3IgZG93bi1yZWd1bGF0ZWQgaW4gb3VyIHRocmVlIHRpc3N1ZXMuIEluIG90aGVyIHdvcmRzLCBpZiB0aGUgc2FtcGxlcyB3ZXJlICJhbm9ueW1vdXMiLCB3ZSdkIGhhdmUgYmVlbiBhYmxlIHRvIGRpc2NvdmVyIGZyb20gd2hpY2ggdGlzc3VlIGVhY2ggc2FtcGxlIHdhcyB0YWtlbiBmcm9tLgoKIyBzZXNzaW9uSW5mbygpCgpUaGlzIGFuYWx5c2lzIHdhcyBjb25kdWN0ZWQgb246CmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAoKIyBCaWJsaW9ncmFwaHkKCkJpb2NvbmR1Y3RvcjsgcmVjb3VudCBxdWljayBzdGFydCBndWlkZSAmIHdvcmtmbG93OyBodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9yZWNvdW50L2luc3QvZG9jL3JlY291bnQtcXVpY2tzdGFydC5odG1sICYgaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2Uvd29ya2Zsb3dzL3ZpZ25ldHRlcy9yZWNvdW50V29ya2Zsb3cvaW5zdC9kb2MvcmVjb3VudC13b3JrZmxvdy5odG1sCgpHaXVsaW8gUGF2ZXNpOyBERSBnZW5lIGFuYWx5c2lzIHdpdGggZWRnZVIgKDIwMjEgVXBkYXRlKTsgaHR0cDovLzE1OS4xNDkuMTYwLjU2L1RyYW5zY3JpcHRvbWljcy9lZGdlUl9leGVyY2lzZS5odG1sCgpCaW9jb25kdWN0b3I7IEVtcGlyaWNhbCBBbmFseXNpcyBvZiBEaWdpdGFsIEdlbmUgRXhwcmVzc2lvbiBEYXRhIGluIFI7IGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvaHRtbC9lZGdlUi5odG1sCgpHaXVsaW8gUGF2ZXNpOyBBbm5vdGF0aW9ucyBFeGVyY2lzZTsgaHR0cDovLzE1OS4xNDkuMTYwLjU2L1RyYW5zY3JpcHRvbWljcy9nYXRhNi5odG1sCgpCaW9jb25kdWN0b3I7IGNsdXN0ZXJQcm9maWxlcjsgaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL2NsdXN0ZXJQcm9maWxlci5odG1sCgpNb2hhbW1lZCBLaGFsZmFuOyBHZW5lIFNldCBFbnJpY2htZW50IEFuYWx5c2lzIHdpdGggQ2x1c3RlclByb2ZpbGVyOyBodHRwczovL2xlYXJuLmdlbmNvcmUuYmlvLm55dS5lZHUvcm5hLXNlcS1hbmFseXNpcy9nZW5lLXNldC1lbnJpY2htZW50LWFuYWx5c2lzLw==